Gson: Come escludere campi specifici dalla serializzazione senza annotazioni

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 = "PF"; 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 firstName o country ma non riesco a riuscire a escludere le proprietà di alcuni campi come country.name .

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

Gradirei qualsiasi aiuto con una soluzione per questo problema.

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

Grazie

Modifica : sto provando a vedere se è ansible emulare il comportamento del plug-in JSON di Struts2

usando Gson

  true  login.password, studentList.*\.sin   

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.name ma non countrOfBirth.name . Inoltre, non voglio escludere Paese come tipo. Quindi i tipi sono gli stessi, è il posto reale nel grafico dell’object che voglio individuare ed escludere.

Qualsiasi campo che non vuoi serializzato in generale dovresti usare il modificatore “transiente”, e questo vale anche per i serializzatori json (almeno lo fa per alcuni che ho usato, incluso gson).

Se non si desidera che il nome venga visualizzato nel file serializzato, assegnargli una parola chiave transitoria, ad esempio:

 private transient String name; 

Maggiori dettagli nella documentazione di Gson

Nishant ha fornito una buona soluzione, ma c’è un modo più semplice. Contrassegna semplicemente i campi desiderati con l’annotazione @Expose, ad esempio:

 @Expose private Long id; 

Lascia tutti i campi che non vuoi serializzare. Quindi crea il tuo object Gson in questo modo:

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

Quindi, vuoi escludere firstName e country.name . Ecco come dovrebbe essere la tua ExclusionStrategy

  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 lo vedi da vicino, restituisce true per Student.firstName e Student.firstName , che è ciò che vuoi escludere.

Devi applicare questa ExclusionStrategy questo 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 restituisce:

{ "MiddleName": "J", "iniziali": "PF", "Cognome": "Fry", "paese": { "id": 91}}

Presumo che l'object country sia inizializzato con id = 91L nella class studentesca.


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

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

Questo ritornerà:

 { "initials": "PF", "country": { "id": 91 }} 

MODIFICA: aggiunte ulteriori informazioni come richiesto.

Questa ExclusionStrategy farà 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": "PF", "lastName": "Fry", "country": { "id": 91 }} 

Dopo aver letto tutte le risposte disponibili ho scoperto che, nel mio caso, la cosa più flessibile era usare l’annotazione personalizzata @Exclude . Quindi, ho implementato una strategia semplice per questo (non volevo contrassegnare tutti i campi usando @Expose né volevo usare transient che era in conflitto con l’in Serializable serializzabile dell’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(); 

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’annotazione @Expose di @Expose con strategie di esclusione personalizzate.

L’unico modo incorporato di utilizzare @Expose è impostare GsonBuilder.excludeFieldsWithoutExposeAnnotation() , ma come indica il nome, i campi senza un esplicito @Expose vengono ignorati. Poiché avevo solo pochi campi da escludere, ho trovato la prospettiva di aggiungere l’annotazione a ogni campo molto macchinoso.

Volevo effettivamente l’inverso, in cui tutto era incluso, a meno che non @Expose esplicitamente @Expose per escluderlo. Ho usato le seguenti strategie di esclusione per realizzare questo:

 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) (si noti che il valore predefinito per entrambi @Expose attributi @Expose è true ). Ovviamente è ansible utilizzare @Expose(serialize = false, deserialize = false) , ma ciò viene eseguito in maniera più concisa dichiarando invece il transient campo (che ha comunque effetto con queste strategie di esclusione personalizzate).

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

 public class GsonFactory { public static Gson build(final List fieldExclusions, final List> 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 (ognuno è facoltativo) e crea il tuo object GSON:

 static { List fieldExclusions = new ArrayList(); fieldExclusions.add("id"); fieldExclusions.add("provider"); fieldExclusions.add("products"); List> classExclusions = new ArrayList>(); classExclusions.add(Product.class); GSON = GsonFactory.build(null, classExclusions); } private static final Gson GSON; public String getSomeJson(){ List list = getEntitiesFromDatabase(); return GSON.toJson(list); } 

Puoi esplorare l’albero JSON con Gson.

Prova qualcosa del genere:

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

Puoi aggiungere anche alcune proprietà:

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

Testato con gson 2.2.4.

Ho risolto questo problema con annotazioni personalizzate. Questa è la mia class 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; } 

Oppure puoi dire quali campi non saranno esposti con:

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

sulla tua class sull’attributo:

 private **transient** boolean nameAttribute; 

Un altro approccio (particolarmente utile se è necessario prendere una decisione per escludere un campo in fase di runtime) è registrare un TypeAdapter con l’istanza di GSON. Esempio di seguito:

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

Nel caso qui sotto, il server si aspetterebbe uno dei due valori ma poiché erano entrambi gli interi Gson li serializzerebbe entrambi. Il mio objective era quello di omettere qualsiasi valore che è zero (o meno) dal json che è pubblicato sul server.

 public class BloodPressurePostSerializer implements JsonSerializer { @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; } } 

Ho usato questa strategia: ho escluso ogni campo che non è contrassegnato con l’ annotazione @SerializedName , cioè:

 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”}

Sto lavorando solo inserendo l’annotazione @Expose , qui la mia versione che utilizzo

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

Nella class del Model :

 @Expose int number; public class AdapterRestApi { 

Nella class Adapter :

 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); }