Gson: So schließen Sie bestimmte Felder ohne Anmerkungen von der Serialisierung aus

413

Ich versuche Gson zu lernen und habe Probleme mit dem Ausschluss von Feldern. Hier sind meine Klassen

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

Ich kann den GsonBuilder verwenden und eine ExclusionStrategy für einen Feldnamen wie firstNameoder hinzufügen, countryaber ich kann es scheinbar nicht schaffen, Eigenschaften bestimmter Felder wie auszuschließen country.name.

Mit der Methode enthält public boolean shouldSkipField(FieldAttributes fa)FieldAttributes nicht genügend Informationen, um das Feld mit einem Filter wie abzugleichen country.name.

PS: Ich möchte Anmerkungen vermeiden, da ich dies verbessern und RegEx zum Herausfiltern von Feldern verwenden möchte.

Bearbeiten : Ich versuche zu sehen, ob es möglich ist, das Verhalten des Struts2 JSON-Plugins zu emulieren

mit Gson

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

Bearbeiten: Ich habe die Frage mit folgendem Zusatz erneut geöffnet:

Ich habe ein zweites Feld mit demselben Typ hinzugefügt, um dieses Problem weiter zu klären. Grundsätzlich möchte ich country.nameaber nicht ausschließen countrOfBirth.name. Ich möchte auch Country nicht als Typ ausschließen. Die Typen sind also die gleichen, es ist die tatsächliche Stelle im Objektdiagramm, die ich lokalisieren und ausschließen möchte.

Liviu T.
quelle
1
Ab Version 2.2 kann ich immer noch keinen Pfad zum auszuschließenden Feld angeben. flexjson.sourceforge.net scheint eine gute Alternative zu sein.
Liviu T.
Schauen Sie sich meine Antwort auf eine ganz ähnliche Frage an. Es basiert auf der Erstellung eines benutzerdefinierten JsonSerializerTyps für einen bestimmten Typ - Countryin Ihrem Fall -, der dann angewendet wird und ExclusionStrategyder entscheidet, welche Felder serialisiert werden sollen.
Pirho

Antworten:

625

Für alle Felder, die Sie im Allgemeinen nicht serialisieren möchten, sollten Sie den Modifikator "transient" verwenden. Dies gilt auch für json-Serialisierer (zumindest für einige, die ich verwendet habe, einschließlich gson).

Wenn Sie nicht möchten, dass der Name im serialisierten JSON angezeigt wird, geben Sie ihm ein vorübergehendes Schlüsselwort, z.

private transient String name;

Weitere Details in der Gson-Dokumentation

Chris Seline
quelle
6
Es ist fast dasselbe wie eine Ausschlussanmerkung, wie sie für alle Instanzen dieser Klasse gilt. Ich wollte einen dynamischen Laufzeitausschluss. In einigen Fällen möchte ich, dass einige Felder ausgeschlossen werden, um eine leichtere / eingeschränkte Antwort zu erhalten, und in anderen möchte ich, dass das vollständige Objekt serialisiert wird
Liviu T.
34
Zu beachten ist, dass vorübergehende Effekte sowohl die Serialisierung als auch die Deserialisierung bewirken. Es gibt auch den Wert von serialisiert in das Objekt aus, selbst wenn er im JSON vorhanden ist.
Kong
3
Das Problem bei der Verwendung transientanstelle von @Exposebesteht darin, dass Sie immer noch ein POJO auf Ihrem Client mit allen Feldern verspotten müssen, die möglicherweise eingehen könnten. Im Fall einer Back-End-API, die möglicherweise von Projekten gemeinsam genutzt wird, kann dies problematisch sein zusätzliche Felder werden hinzugefügt. Im Wesentlichen handelt es sich um Whitelisting oder Blacklisting der Felder.
Theblang
11
Dieser Ansatz hat bei mir nicht funktioniert, da er nicht nur das Feld von der gson-Serialisierung ausschloss, sondern das Feld auch von der internen App-Serialisierung ausschloss (über die serialisierbare Schnittstelle).
pkk
8
transient verhindert die SERIALISIERUNG und DESERIALISIERUNG eines Feldes. Dies entspricht nicht meinen Bedürfnissen.
Loenix
318

Nishant bot eine gute Lösung, aber es gibt einen einfacheren Weg. Markieren Sie einfach die gewünschten Felder mit der Annotation @Expose, z.

@Expose private Long id;

Lassen Sie alle Felder weg, die Sie nicht serialisieren möchten. Erstellen Sie dann einfach Ihr Gson-Objekt folgendermaßen:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
JayPea
quelle
95
Ist es möglich, so etwas wie "notExpose" zu haben und diese nur für den Fall zu ignorieren, dass alle bis auf ein Feld serialisiert werden müssen und das Hinzufügen von Anmerkungen zu allen überflüssig ist?
Daniil Shevelev
2
@DaSh Ich hatte vor kurzem ein solches Szenario. Es war sehr einfach, eine benutzerdefinierte ExclusionStrategy zu schreiben, die genau das tat. Siehe Nishants Antwort. Das einzige Problem war, eine Reihe von Containerklassen einzuschließen und mit skipclass vs skipfield zu fummeln (Felder können Klassen sein ...)
Keyser
1
@DaSh Meine Antwort unten macht genau das.
Derek Shockey
Was für eine großartige Lösung. Ich hatte ein Szenario, in dem ein Feld auf die Festplatte serialisiert werden soll, aber ignoriert werden soll, wenn es über gson an einen Server gesendet wird. Perfekt danke!
Slynk
1
@Danlil Sie sollten in der Lage sein, @Expose (serialize = false, deserialize = false) zu verwenden
Hrk
237

Also, wollen Sie ausschließen firstName und country.name. So ExclusionStrategysolltest du aussehen

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

    }

Wenn Sie genau sehen, kehrt es truefür Student.firstNameund zurückCountry.name , was Sie ausschließen möchten.

Sie müssen dies ExclusionStrategyso anwenden ,

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

Dies gibt zurück:

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

Ich gehe davon aus, dass das Länderobjekt id = 91Lin der Schülerklasse initialisiert wird .


Sie können Phantasie bekommen. Beispielsweise möchten Sie kein Feld serialisieren, dessen Name die Zeichenfolge "name" enthält. Mach das:

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

Dies wird zurückkehren:

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

BEARBEITEN: Weitere Informationen wie gewünscht hinzugefügt.

Dies ExclusionStrategywird die Sache tun, aber Sie müssen "Vollqualifizierter Feldname" übergeben. Siehe unten:

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

    }

Hier ist, wie wir es generisch verwenden können.

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

Es gibt zurück:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}
Nishant
quelle
Vielen Dank für Ihre Antwort. Ich möchte eine ExclusionStrategy erstellen, die eine Zeichenfolge wie country.namefolgt und das Feld nur namebeim Serialisieren des Felds ausschließt country. Es sollte allgemein genug sein, um auf jede Klasse angewendet zu werden, die eine Eigenschaft mit dem Namen country of the Country class hat. Ich möchte nicht für jede Klasse eine ExclusionStrategy erstellen
Liviu T.
@Liviu T. Ich habe die Antwort aktualisiert. Das ist ein allgemeiner Ansatz. Sie werden vielleicht noch kreativer, aber ich habe es elementar gehalten.
Nishant
Ty für das Update. Was ich versuche zu sehen, ob es möglich ist zu wissen, wo im Objektdiagramm ich bin, wenn die Methode aufgerufen wird, damit ich einige Felder des Landes ausschließen kann, aber nicht countryOfBirth (zum Beispiel), also dieselbe Klasse, aber unterschiedliche Eigenschaften. Ich habe meine Frage bearbeitet, um zu klären, was ich erreichen
möchte
Gibt es eine Möglichkeit, Felder mit leeren Werten auszuschließen?
Yusuf K.
12
Diese Antwort sollte als bevorzugte Antwort markiert werden. Im Gegensatz zu den anderen Antworten, die derzeit mehr Stimmen haben, müssen Sie bei dieser Lösung die Bean-Klasse nicht ändern. Das ist ein großes Plus. Was wäre, wenn jemand anderes dieselbe Bean-Klasse verwenden würde und Sie ein Feld markieren würden, das er als "vorübergehend" beibehalten wollte?
user64141
221

Nachdem ich alle verfügbaren Antworten gelesen hatte, stellte ich fest, dass es in meinem Fall am flexibelsten war, benutzerdefinierte @ExcludeAnmerkungen zu verwenden. Also habe ich eine einfache Strategie dafür implementiert (ich wollte nicht alle Felder mit markieren @Exposeoder verwenden, transientdie in Konflikt mit der App- SerializableSerialisierung stehen):

Anmerkung:

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

Strategie:

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

Verwendungszweck:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
pkk
quelle
16
Als zusätzliche Anmerkung, wenn Sie Ihre Ausschlussstrategie nur für die Serialisierung oder nur für die Deserialisierung verwenden möchten, verwenden Sie: addSerializationExclusionStrategyoder addDeserializationExclusionStrategyanstelle vonsetExclusionStrategies
GLee
9
Perfekt! Die vorübergehende Lösung funktioniert nicht für mich, weil ich Realm für DB verwende und ein Feld nur von Gson ausschließen möchte, aber nicht Realm (was vorübergehend geschieht)
Marcio Granzotto
3
Dies sollte die akzeptierte Antwort sein. Um Nullfelder zu ignorieren, wechseln Sie einfach f.getAnnotation(Exclude.class) != nullzuf.getAnnotation(Exclude.class) == null
Sharp Edge
3
Hervorragend geeignet, wenn Sie es transientaufgrund der Anforderungen anderer Bibliotheken nicht verwenden können . Vielen Dank!
Martin D
1
Auch toll für mich, weil Android meine Klassen zwischen Aktivitäten serialisiert, aber ich möchte nur, dass sie ausgeschlossen werden, wenn ich GSON verwende. Dadurch funktioniert meine App so lange, bis sie eingepackt und an andere gesendet werden soll.
ThePartyTurtle
81

Ich bin auf dieses Problem gestoßen, bei dem ich eine kleine Anzahl von Feldern hatte, die ich nur von der Serialisierung ausschließen wollte. Deshalb habe ich eine ziemlich einfache Lösung entwickelt, die Gsons @ExposeAnnotation mit benutzerdefinierten Ausschlussstrategien verwendet.

Die einzige integrierte Möglichkeit besteht darin , Felder ohne explizite @ExposeEinstellung festzulegen GsonBuilder.excludeFieldsWithoutExposeAnnotation(), aber wie der Name schon sagt@Expose ignoriert. Da ich nur wenige Felder hatte, die ich ausschließen wollte, fand ich die Aussicht, die Anmerkung zu jedem Feld hinzuzufügen, sehr umständlich.

Ich wollte effektiv das Inverse, in dem alles enthalten war, es sei denn, ich habe es ausdrücklich @Exposeausgeschlossen. Ich habe die folgenden Ausschlussstrategien verwendet, um dies zu erreichen:

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

Jetzt kann ich einige Felder mit @Expose(serialize = false)oder @Expose(deserialize = false)Anmerkungen einfach ausschließen (beachten Sie, dass der Standardwert für beide @ExposeAttribute ist true). Sie können natürlich verwenden @Expose(serialize = false, deserialize = false), aber dies wird präziser erreicht, indem Sie transientstattdessen das Feld deklarieren (was bei diesen benutzerdefinierten Ausschlussstrategien immer noch wirksam ist).

Derek Shockey
quelle
Aus Gründen der Effizienz kann ich einen Fall für die Verwendung von @Expose (serialize = false, deserialize = false) anstelle von transient sehen.
Paiego
1
@paiego Kannst du das erweitern? Ich bin jetzt viele Jahre von der Verwendung von Gson entfernt und verstehe nicht, warum die Anmerkung effizienter ist als das Markieren als vorübergehend.
Derek Shockey
Ahh, ich habe einen Fehler gemacht, danke, dass du das verstanden hast. Ich verwechselte flüchtig mit vorübergehend. (zB Es gibt kein Caching und daher kein Cache-Kohärenzproblem mit flüchtigen, aber es ist weniger performant) Wie auch immer, Ihr Code hat großartig funktioniert!
Paiego
18

Sie können den Json-Baum mit Gson erkunden.

Versuchen Sie so etwas:

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

Sie können auch einige Eigenschaften hinzufügen:

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

Getestet mit gson 2.2.4.

Klaudia Rzewnicka
quelle
3
Ich frage mich, ob dies ein zu großer Leistungseinbruch ist, wenn Sie eine komplexe Eigenschaft entfernen möchten, die vor dem Entfernen analysiert werden muss. Gedanken?
Ben
Stellen Sie sich definitiv keine skalierbare Lösung vor. Stellen Sie sich alle Kopfschmerzen vor, die Sie durchmachen müssen, wenn Sie die Struktur Ihres Objekts ändern oder Dinge hinzufügen / entfernen.
Codenamezero
16

Ich habe mir eine Klassenfabrik ausgedacht, um diese Funktionalität zu unterstützen. Übergeben Sie eine beliebige Kombination von Feldern oder Klassen, die Sie ausschließen möchten.

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

    }
}

Erstellen Sie zur Verwendung zwei Listen (jede ist optional) und erstellen Sie Ihr GSON-Objekt:

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);
}
Domenic D.
quelle
Natürlich kann dies geändert werden, um den vollständig qualifizierten Namen des Attributs
Domenic D.
Ich mache unten Beispiel. Das funktioniert nicht. Pls schlagen private statische endgültige Gson GSON vor; statisch {List <String> fieldExclusions = new ArrayList <String> (); fieldExclusions.add ("id"); GSON = GsonFactory.build (fieldExclusions, null); } private statische Zeichenfolge getSomeJson () {String jsonStr = "[{" id ": 111," name ":" praveen "," age ": 16}, {" id ": 222, "Name": "Prashant", "Alter": 20}] "; return jsonStr; } public static void main (String [] args) {String jsonStr = getSomeJson (); System.out.println (GSON.toJson (jsonStr)); }
Praveen Hiremath
13

Ich habe dieses Problem mit benutzerdefinierten Anmerkungen gelöst. Dies ist meine Annotationsklasse "SkipSerialisation":

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

}

und das ist mein 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;
  }
});

Beispiel:

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}
Hibbem
quelle
5
Gson: Wie man bestimmte Felder ohne Anmerkungen
Ben
3
Sie sollten auch @Retention(RetentionPolicy.RUNTIME)Ihre Anmerkung hinzufügen .
David Novák
9

Oder kann sagen, was Felder nicht aussetzen mit:

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

auf Ihrer Klasse auf Attribut:

private **transient** boolean nameAttribute;
lucasddaniel
quelle
17
Transiente und statische Felder sind standardmäßig ausgeschlossen. das muss man nicht anrufen excludeFieldsWithModifiers().
Derek Shockey
9

Ich habe diese Strategie angewendet: Ich habe jedes Feld ausgeschlossen, das nicht mit der Annotation @SerializedName gekennzeichnet ist , dh:

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

Es kehrt zurück

{"VisibleValue": "Ich werde das sehen"}

MadMurdok
quelle
6

Ein anderer Ansatz (besonders nützlich, wenn Sie eine Entscheidung zum Ausschließen eines Felds zur Laufzeit treffen müssen) besteht darin, einen TypeAdapter bei Ihrer gson-Instanz zu registrieren. Beispiel unten:

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

Im folgenden Fall würde der Server einen von zwei Werten erwarten, aber da beide Ints waren, würde gson beide serialisieren. Mein Ziel war es, jeden Wert, der Null (oder weniger) ist, aus dem auf dem Server veröffentlichten JSON wegzulassen.

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;
    }
}
Damian
quelle
6

Kotlins @TransientAnmerkung macht anscheinend auch den Trick.

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

Ausgabe:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}
Lookashc
quelle
1

Ich arbeite nur, indem ich die @ExposeAnmerkung einfüge, hier meine Version, die ich benutze

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

In der ModelKlasse:

@Expose
int number;

public class AdapterRestApi {

In der AdapterKlasse:

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);
}
Devitoper
quelle
1

Ich habe Kotlin-Version

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

und wie Sie dies zu Retrofit GSONConverterFactory hinzufügen können:

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

Das was ich immer benutze:

Das in Gson implementierte Standardverhalten ist, dass Nullobjektfelder ignoriert werden.

Bedeutet, dass das Gson-Objekt keine Felder mit Nullwerten für JSON serialisiert. Wenn ein Feld in einem Java-Objekt null ist, schließt Gson es aus.

Mit dieser Funktion können Sie ein Objekt in null konvertieren oder es selbst festlegen

     /**
   * 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);
  }
Julian Solarte
quelle