Gson: come escludere campi specifici dalla serializzazione senza annotazioni


413

Sto cercando di imparare Gson e sto lottando con l'esclusione sul campo. Ecco le mie lezioni

public class Student {    
  private Long                id;
  private String              firstName        = "Philip";
  private String              middleName       = "J.";
  private String              initials         = "P.F";
  private String              lastName         = "Fry";
  private Country             country;
  private Country             countryOfBirth;
}

public class Country {    
  private Long                id;
  private String              name;
  private Object              other;
}

Posso usare GsonBuilder e aggiungere una ExclusionStrategy per un nome di campo come firstNameo countryma non riesco a riuscire a escludere proprietà di determinati campi come country.name.

Utilizzando il metodo public boolean shouldSkipField(FieldAttributes fa), FieldAttributes non contiene informazioni sufficienti per abbinare il campo a un filtro come country.name.

PS: Voglio evitare le annotazioni poiché voglio migliorare questo aspetto e utilizzare RegEx per filtrare i campi.

Modifica : sto cercando di vedere se è possibile emulare il comportamento del plug-in JSON di Struts2

usando Gson

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>

Modifica: ho riaperto la domanda con la seguente aggiunta:

Ho aggiunto un secondo campo con lo stesso tipo per chiarire ulteriormente questo problema. Fondamentalmente voglio escludere country.namema non countrOfBirth.name. Inoltre, non voglio escludere Paese come tipo. Quindi i tipi sono gli stessi, è il posto effettivo nel grafico a oggetti che voglio individuare ed escludere.


1
Ancora dalla versione 2.2 non riesco ancora a specificare un percorso verso il campo da escludere. flexjson.sourceforge.net sembra una buona alternativa.
Liviu T.

Dai un'occhiata alla mia risposta a una domanda abbastanza simile. Si basa sulla creazione di un'abitudine JsonSerializerper un certo tipo - Countrynel tuo caso - per cui quindi viene applicato un ExclusionStrategyche decide quali campi serializzare.
Pirho,

Risposte:


625

Tutti i campi che non vuoi serializzare in generale dovresti usare il modificatore "transitorio", e questo vale anche per i serializzatori json (almeno lo fa per alcuni che ho usato, incluso gson).

Se non desideri che il nome venga visualizzato nel json serializzato, assegnagli una parola chiave temporanea, ad esempio:

private transient String name;

Maggiori dettagli nella documentazione Gson


6
è quasi la stessa cosa di un'annotazione di esclusione in quanto si applica a tutte le istanze di quella classe. Volevo l'esclusione dinamica di runtime. In alcuni casi desidero escludere alcuni campi per fornire una risposta più leggera / limitata e in altri desidero serializzare l'intero oggetto
Liviu T.

34
Una cosa da notare è che i transitori hanno effetti sia sulla serializzazione che sulla deserializzazione. Emetterà inoltre il valore da serializzato nell'oggetto, anche se è presente in JSON.
Kong,

3
Il problema con l'utilizzo transientinvece di @Exposeè che devi ancora deridere un POJO sul tuo client con tutti i campi che potrebbero eventualmente entrare. Nel caso di un'API back-end che potrebbe essere condivisa tra i progetti, questo potrebbe essere problematico nel caso campi aggiuntivi vengono aggiunti. Essenzialmente si tratta di whitelisting vs blacklist dei campi.
theblang,

11
Questo approccio non ha funzionato per me, in quanto non solo ha escluso il campo dalla serializzazione gson, ma ha escluso il campo dalla serializzazione interna dell'app (utilizzando l'interfaccia Serializable).
pkk,

8
transitorio impedisce la SERIALIZZAZIONE e la DESERIALIZZAZIONE di un campo. Questo non corrisponde alle mie esigenze.
Loenix,

318

Nishant ha fornito una buona soluzione, ma esiste un modo più semplice. È sufficiente contrassegnare i campi desiderati con l'annotazione @Expose, ad esempio:

@Expose private Long id;

Tralascia tutti i campi che non si desidera serializzare. Quindi crea il tuo oggetto Gson in questo modo:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

95
È possibile avere qualcosa come "notExpose" e ignorare solo quelli nel caso in cui tutti i campi tranne uno debbano essere serializzati e l'aggiunta di annotazioni a tutti loro sia ridondante?
Daniil Shevelev,

2
@DaSh Di recente ho avuto uno scenario del genere. È stato molto facile scrivere una ExclusionStrategy personalizzata che ha fatto esattamente questo. Vedi la risposta di Nishant. L'unico problema era includere un gruppo di classi container e suonare il violino con skipclass vs skipfield (i campi possono essere classi ...)
keyser

1
@DaSh La mia risposta qui sotto fa esattamente questo.
Derek Shockey,

Che ottima soluzione. Mi imbattevo in uno scenario in cui volevo un campo serializzato su disco ma da ignorare quando lo inviavo a un server tramite gson. Perfetto grazie!
Slynk,

1
@Danlil Dovresti essere in grado di utilizzare @Expose (serialize = false, deserialize = false)
Hrk,

238

Quindi, vuoi escludere firstName e country.name. Ecco come ExclusionStrategydovrebbe apparire

    public class TestExclStrat implements ExclusionStrategy {

        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
            (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
        }

    }

Se vedi da vicino, ritorna trueper Student.firstNamee Country.name, che è ciò che vuoi escludere.

Devi applicare questo in ExclusionStrategyquesto modo,

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat())
        //.serializeNulls() <-- uncomment to serialize NULL fields as well
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json);

Questo ritorna:

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

Presumo che l'oggetto paese sia inizializzato con id = 91Lnella classe studente.


Potresti avere fantasia. Ad esempio, non si desidera serializzare alcun campo che contenga una stringa "name" nel suo nome. Fai questo:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name"); 
}

Questo restituirà:

{ "initials": "P.F", "country": { "id": 91 }}

EDIT: aggiunte ulteriori informazioni come richiesto.

Questo ExclusionStrategyfarà la cosa, ma è necessario passare "Nome campo completo". Vedi sotto:

    public class TestExclStrat implements ExclusionStrategy {

        private Class<?> c;
        private String fieldName;
        public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
        {
            this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
            this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
        }
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
        }

    }

Ecco come possiamo usarlo genericamente.

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
        //.serializeNulls()
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json); 

Restituisce:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}

Grazie per la tua risposta Quello che voglio è creare una ExclusionStrategy che può prendere una stringa come country.nameed escludere il campo solo namequando si serializza il campo country. Dovrebbe essere abbastanza generico da applicare a ogni classe che ha una proprietà denominata country della classe Country. Non voglio creare una ExclusionStrategy per ogni classe
Liviu T.

@Liviu T. Ho aggiornato la risposta. Questo ha un approccio generico. Potresti diventare ancora più creativo, ma l'ho mantenuto elementare.
Nishant,

Ty per l'aggiornamento. Quello che sto provando a vedere se è possibile sapere dove sono nel grafico dell'oggetto quando il metodo chiamato così posso escludere alcuni campi di paese ma non countryOfBirth (ad esempio) quindi stessa classe ma proprietà diverse. Ho modificato la mia domanda per chiarire cosa sto cercando di ottenere
Liviu T.

C'è un modo per escludere campi con valori vuoti?
Yusuf K.,

12
Questa risposta dovrebbe essere contrassegnata come risposta preferita. A differenza delle altre risposte che attualmente hanno più voti, questa soluzione non richiede di modificare la classe bean. Questo è un grande vantaggio. E se qualcun altro stesse usando la stessa classe bean e tu contrassegnassi un campo che volevano persistere come "transitorio"?
user64141

221

Dopo aver letto tutte le risposte disponibili ho scoperto che, nel mio caso, il più flessibile era usare l' @Excludeannotazione personalizzata . Quindi, ho implementato una strategia semplice per questo (non volevo contrassegnare tutti i campi usando @Exposené volevo usare quelli in transientconflitto con la Serializableserializzazione delle app ):

Annotazione:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

Strategia:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

Uso:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();

16
Come nota aggiuntiva, se si desidera utilizzare la propria strategia di esclusione solo per serializzazione o solo deserializzazione, utilizzare: addSerializationExclusionStrategyo addDeserializationExclusionStrategyinvece disetExclusionStrategies
GLee

9
Perfetto! La soluzione temporanea non funziona per me perché sto usando Realm per DB e voglio escludere un campo solo da Gson, ma non Realm (che fa transitorio)
Marcio Granzotto,

3
Questa dovrebbe essere la risposta accettata. Per ignorare i campi null basta cambiare f.getAnnotation(Exclude.class) != nullinf.getAnnotation(Exclude.class) == null
Sharp Edge

3
Eccellente quando non è possibile utilizzare a transientcausa delle esigenze di altre librerie. Grazie!
Martin D,

1
Ottimo anche per me perché Android serializza le mie lezioni tra le attività, ma le voglio escluse solo quando utilizzo GSON. In questo modo la mia app continuerà a funzionare allo stesso modo fino a quando non vorrà chiuderla per inviarla ad altri.
ThePartyTurtle,

81

Mi sono imbattuto in questo problema, in cui avevo un piccolo numero di campi che volevo escludere solo dalla serializzazione, quindi ho sviluppato una soluzione abbastanza semplice che utilizza l' @Exposeannotazione di Gson con strategie di esclusione personalizzate.

L'unico modo integrato da utilizzare @Exposeè l'impostazione GsonBuilder.excludeFieldsWithoutExposeAnnotation(), ma come indica il nome, i campi senza esplicito @Exposevengono ignorati. Dato che avevo solo pochi campi che volevo escludere, ho trovato molto ingombrante la prospettiva di aggiungere l'annotazione ad ogni campo.

Volevo effettivamente l'inverso, in cui tutto era incluso a meno che non lo usassi esplicitamente @Exposeper escluderlo. Ho usato le seguenti strategie di esclusione per raggiungere questo obiettivo:

new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.serialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .addDeserializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.deserialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .create();

Ora posso facilmente escludere alcuni campi con @Expose(serialize = false)o @Expose(deserialize = false)annotazioni (si noti che il valore predefinito per entrambi gli @Exposeattributi è true). Ovviamente puoi usare @Expose(serialize = false, deserialize = false), ma questo è più concisamente realizzato dichiarando transientinvece il campo (che ha ancora effetto con queste strategie di esclusione personalizzate).


Per motivi di efficienza, posso vedere un caso per l'utilizzo di @Expose (serialize = false, deserialize = false) anziché transitorio.
paiego,

1
@paiego Puoi approfondire questo? Sono ormai molti anni che non uso Gson e non capisco perché l'annotazione sia più efficiente del contrassegnarla come transitoria.
Derek Shockey,

Ah, ho fatto un errore, grazie per averlo colto. Ho scambiato volatile per transitorio. (ad es. non c'è cache e quindi nessun problema di coerenza della cache con volatile, ma è meno performante) Comunque, il tuo codice ha funzionato alla grande!
paiego

18

Puoi esplorare l'albero JSON con Gson.

Prova qualcosa del genere:

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");

Puoi anche aggiungere alcune proprietà:

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);

Testato con gson 2.2.4.


3
Mi chiedo se questo è un grande successo se si vuole sbarazzarsi di una proprietà complessa che deve essere analizzata prima della rimozione. Pensieri?
Ben

Sicuramente non è una soluzione scalabile, immagina tutto il mal di testa che dovrai attraversare se cambi la struttura del tuo oggetto o aggiungi / rimuovi roba.
codenamezero

16

Mi è venuta in mente una fabbrica di classe per supportare questa funzionalità. Passa in qualsiasi combinazione di campi o classi che desideri escludere.

public class GsonFactory {

    public static Gson build(final List<String> fieldExclusions, final List<Class<?>> classExclusions) {
        GsonBuilder b = new GsonBuilder();
        b.addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return fieldExclusions == null ? false : fieldExclusions.contains(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return classExclusions == null ? false : classExclusions.contains(clazz);
            }
        });
        return b.create();

    }
}

Per utilizzare, crea due elenchi (ciascuno è facoltativo) e crea il tuo oggetto GSON:

static {
 List<String> fieldExclusions = new ArrayList<String>();
 fieldExclusions.add("id");
 fieldExclusions.add("provider");
 fieldExclusions.add("products");

 List<Class<?>> classExclusions = new ArrayList<Class<?>>();
 classExclusions.add(Product.class);
 GSON = GsonFactory.build(null, classExclusions);
}

private static final Gson GSON;

public String getSomeJson(){
    List<Provider> list = getEntitiesFromDatabase();
    return GSON.toJson(list);
}

Naturalmente, questo può essere modificato per esaminare il nome completo dell'attributo ed escluderlo in caso di corrispondenza ...
Domenic D.

Sto facendo sotto esempio. Questo non sta funzionando. Pls suggerisce Gson GSON finale statico privato; static {List <String> fieldExclusions = new ArrayList <String> (); fieldExclusions.add ( "id"); GSON = GsonFactory.build (fieldExclusions, null); } stringa statica privata getSomeJson () {String jsonStr = "[{\" id \ ": 111, \" name \ ": \" praveen \ ", \" age \ ": 16}, {\" id \ ": 222, \ "nome \": \ "Prashant \", \ "età \": 20}] "; return jsonStr; } public static void main (String [] args) {String jsonStr = getSomeJson (); System.out.println (GSON.toJson (jsonStr)); }
Praveen Hiremath il

13

Ho risolto questo problema con annotazioni personalizzate. Questa è la mia classe di annotazione "SkipSerialisation":

@Target (ElementType.FIELD)
public @interface SkipSerialisation {

}

e questo è il mio GsonBuilder:

gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() {

  @Override public boolean shouldSkipField (FieldAttributes f) {

    return f.getAnnotation(SkipSerialisation.class) != null;

  }

  @Override public boolean shouldSkipClass (Class<?> clazz) {

    return false;
  }
});

Esempio :

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}

5
Gson: Come escludere campi specifici dalla serializzazione senza annotazioni
Ben

3
Dovresti anche aggiungere @Retention(RetentionPolicy.RUNTIME)alla tua annotazione.
David Novák,

9

Oppure puoi dire quali campi non verranno esposti con:

Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();

sulla tua classe sull'attributo:

private **transient** boolean nameAttribute;

17
I campi transitori e statici sono esclusi per impostazione predefinita; non è necessario chiamarlo excludeFieldsWithModifiers().
Derek Shockey,

9

Ho usato questa strategia: ho escluso tutti i campi che non sono contrassegnati con l' annotazione @SerializedName , ovvero:

public class Dummy {

    @SerializedName("VisibleValue")
    final String visibleValue;
    final String hiddenValue;

    public Dummy(String visibleValue, String hiddenValue) {
        this.visibleValue = visibleValue;
        this.hiddenValue = hiddenValue;
    }
}


public class SerializedNameOnlyStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(SerializedName.class) == null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}


Gson gson = new GsonBuilder()
                .setExclusionStrategies(new SerializedNameOnlyStrategy())
                .create();

Dummy dummy = new Dummy("I will see this","I will not see this");
String json = gson.toJson(dummy);

Ritorna

{"VisibleValue": "Vedrò questo"}


6

Un altro approccio (particolarmente utile se è necessario prendere una decisione per escludere un campo in fase di esecuzione) è quello di registrare un TypeAdapter con l'istanza gson. Esempio sotto:

Gson gson = new GsonBuilder()
.registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer())

Nel caso seguente, il server si aspetterebbe uno dei due valori, ma poiché erano entrambi ints, gson li avrebbe serializzati entrambi. Il mio obiettivo era quello di omettere qualsiasi valore che sia zero (o inferiore) dal json che è pubblicato sul server.

public class BloodPressurePostSerializer implements JsonSerializer<BloodPressurePost> {

    @Override
    public JsonElement serialize(BloodPressurePost src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();

        if (src.systolic > 0) {
            jsonObject.addProperty("systolic", src.systolic);
        }

        if (src.diastolic > 0) {
            jsonObject.addProperty("diastolic", src.diastolic);
        }

        jsonObject.addProperty("units", src.units);

        return jsonObject;
    }
}

6

Anche l' @Transientannotazione di Kotlin sembra fare il trucco.

data class Json(
    @field:SerializedName("serialized_field_1") val field1: String,
    @field:SerializedName("serialized_field_2") val field2: String,
    @Transient val field3: String
)

Produzione:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}

1

Sto lavorando semplicemente inserendo l' @Exposeannotazione, qui la mia versione che uso

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

In Modelclasse:

@Expose
int number;

public class AdapterRestApi {

Nella Adapterclasse:

public EndPointsApi connectRestApi() {
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(90000, TimeUnit.SECONDS)
            .readTimeout(90000,TimeUnit.SECONDS).build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ConstantRestApi.ROOT_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();

    return retrofit.create  (EndPointsApi.class);
}

1

Ho la versione di Kotlin

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
internal annotation class JsonSkip

class SkipFieldsStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes): Boolean {
        return f.getAnnotation(JsonSkip::class.java) != null
    }
}

e come puoi aggiungerlo a Retrofit GSONConverterFactory:

val gson = GsonBuilder()
                .setExclusionStrategies(SkipFieldsStrategy())
                //.serializeNulls()
                //.setDateFormat(DateFormat.LONG)
                //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                //.setPrettyPrinting()
                //.registerTypeAdapter(Id.class, IdTypeAdapter())
                .create()
        return GsonConverterFactory.create(gson)

0

Questo è quello che uso sempre:

Il comportamento predefinito implementato in Gson è che i campi oggetto null vengono ignorati.

Significa che l'oggetto Gson non serializza i campi con valori null su JSON. Se un campo in un oggetto Java è nullo, Gson lo esclude.

È possibile utilizzare questa funzione per convertire alcuni oggetti in null o ben impostati da soli

     /**
   * convert object to json
   */
  public String toJson(Object obj) {
    // Convert emtpy string and objects to null so we don't serialze them
    setEmtpyStringsAndObjectsToNull(obj);
    return gson.toJson(obj);
  }

  /**
   * Sets all empty strings and objects (all fields null) including sets to null.
   *
   * @param obj any object
   */
  public void setEmtpyStringsAndObjectsToNull(Object obj) {
    for (Field field : obj.getClass().getDeclaredFields()) {
      field.setAccessible(true);
      try {
        Object fieldObj = field.get(obj);
        if (fieldObj != null) {
          Class fieldType = field.getType();
          if (fieldType.isAssignableFrom(String.class)) {
            if(fieldObj.equals("")) {
              field.set(obj, null);
            }
          } else if (fieldType.isAssignableFrom(Set.class)) {
            for (Object item : (Set) fieldObj) {
              setEmtpyStringsAndObjectsToNull(item);
            }
            boolean setFielToNull = true;
            for (Object item : (Set) field.get(obj)) {
              if(item != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          } else if (!isPrimitiveOrWrapper(fieldType)) {
            setEmtpyStringsAndObjectsToNull(fieldObj);
            boolean setFielToNull = true;
            for (Field f : fieldObj.getClass().getDeclaredFields()) {
              f.setAccessible(true);
              if(f.get(fieldObj) != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          }
        }
      } catch (IllegalAccessException e) {
        System.err.println("Error while setting empty string or object to null: " + e.getMessage());
      }
    }
  }

  private void setFieldToNull(Object obj, Field field) throws IllegalAccessException {
    if(!Modifier.isFinal(field.getModifiers())) {
      field.set(obj, null);
    }
  }

  private boolean isPrimitiveOrWrapper(Class fieldType)  {
    return fieldType.isPrimitive()
        || fieldType.isAssignableFrom(Integer.class)
        || fieldType.isAssignableFrom(Boolean.class)
        || fieldType.isAssignableFrom(Byte.class)
        || fieldType.isAssignableFrom(Character.class)
        || fieldType.isAssignableFrom(Float.class)
        || fieldType.isAssignableFrom(Long.class)
        || fieldType.isAssignableFrom(Double.class)
        || fieldType.isAssignableFrom(Short.class);
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.