Felder von der Serialisierung in Gson ausschließen

1. Übersicht

In diesem kurzen Tutorial werden wir die verfügbaren Optionen zum Ausschließen eines oder mehrerer Felder einer Java-Klasse und ihrer Unterklassen von der Gson-Serialisierung untersuchen.

2. Ersteinrichtung

Definieren wir zunächst unsere Klassen:

@Data @AllArgsConstructor public class MyClass { private long id; private String name; private String other; private MySubClass subclass; } @Data @AllArgsConstructor public class MySubClass { private long id; private String description; private String otherVerboseInfo; } 

Wir haben sie der Einfachheit halber mit Lombok kommentiert (syntaktischer Zucker für Getter, Setter, Konstrukteure…).

Lassen Sie uns sie jetzt füllen:

MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize") MyClass source = new MyClass(1L, "foo", "bar", subclass); 

Unser Ziel ist es, zu verhindern, dass die Felder MyClass.other und MySubClass.otherVerboseInfo serialisiert werden.

Die Ausgabe, die wir erwarten, ist:

{ "id":1, "name":"foo", "subclass":{ "id":42, "description":"the answer" } } 

In Java:

String expectedResult = "{\"id\":1,\"name\":\"foo\",\"subclass\":{\"id\":42,\"description\":\"the answer\"}}"; 

3. Transient Modifier

Wir können ein Feld mit dem Transientenmodifikator markieren :

public class MyClass { private long id; private String name; private transient String other; private MySubClass subclass; } public class MySubClass { private long id; private String description; private transient String otherVerboseInfo; } 

Der Gson-Serializer ignoriert jedes als vorübergehend deklarierte Feld:

String jsonString = new Gson().toJson(source); assertEquals(expectedResult, jsonString); 

Dies ist zwar sehr schnell, hat aber auch einen schwerwiegenden Nachteil: Jedes Serialisierungstool berücksichtigt vorübergehend , nicht nur Gson.

Transient ist die Java-Methode, um von der Serialisierung auszuschließen. Dann wird unser Feld auch durch die Serialisierung von Serializable und durch jedes Bibliothekstool oder Framework, das unsere Objekte verwaltet, herausgefiltert.

Darüber hinaus funktioniert das Schlüsselwort transient immer sowohl für die Serialisierung als auch für die Deserialisierung, was je nach Anwendungsfall einschränkend sein kann.

4. @Expose Annotation

Gson com.google.gson.annotations @Expose Annotation funktioniert umgekehrt.

Wir können damit deklarieren, welche Felder serialisiert werden sollen, und die anderen ignorieren:

public class MyClass { @Expose private long id; @Expose private String name; private String other; @Expose private MySubClass subclass; } public class MySubClass { @Expose private long id; @Expose private String description; private String otherVerboseInfo; } 

Dazu müssen wir Gson mit einem GsonBuilder instanziieren:

Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); String jsonString = gson.toJson(source); assertEquals(expectedResult, jsonString); 

Dieses Mal können wir auf Feldebene steuern, ob die Filterung für Serialisierung, Deserialisierung oder beides erfolgen soll (Standard).

Lassen Sie uns sehen, wie Sie verhindern können , dass MyClass.other serialisiert wird, aber dass es während einer Deserialisierung von JSON gefüllt wird:

@Expose(serialize = false, deserialize = true) private String other; 

Dies ist zwar der einfachste von Gson bereitgestellte Weg und wirkt sich nicht auf andere Bibliotheken aus. Dies kann jedoch zu Redundanz im Code führen. Wenn wir eine Klasse mit hundert Feldern haben und nur ein Feld ausschließen möchten, müssen wir neunundneunzig Anmerkungen schreiben, was übertrieben ist.

5. ExclusionStrategy

Eine hochgradig anpassbare Lösung ist die Verwendung von com.google.gson. ExclusionStrategy .

Es ermöglicht uns, (extern oder mit einer anonymen inneren Klasse) eine Strategie zu definieren, um den GsonBuilder anzuweisen, ob Felder (und / oder Klassen) mit benutzerdefinierten Kriterien serialisiert werden sollen.

Gson gson = new GsonBuilder() .addSerializationExclusionStrategy(strategy) .create(); String jsonString = gson.toJson(source); assertEquals(expectedResult, jsonString); 

Sehen wir uns einige Beispiele für intelligente Strategien an.

5.1. Mit Klassen- und Feldnamen

Natürlich können wir auch einen oder mehrere Felder / Klassennamen fest codieren:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes field) { if (field.getDeclaringClass() == MyClass.class && field.getName().equals("other")) { return true; } if (field.getDeclaringClass() == MySubClass.class && field.getName().equals("otherVerboseInfo")) { return true; } return false; } @Override public boolean shouldSkipClass(Class clazz) { return false; } }; 

Dies ist schnell und direkt auf den Punkt gebracht, aber nicht sehr wiederverwendbar und auch fehleranfällig, falls wir unsere Attribute umbenennen.

5.2. Mit Geschäftskriterien

Da wir lediglich einen Booleschen Wert zurückgeben müssen, können wir jede Geschäftslogik, die wir mögen, in diese Methode implementieren.

Im folgenden Beispiel identifizieren wir jedes Feld, das mit "other" beginnt, als Felder, die nicht serialisiert werden sollten, unabhängig von der Klasse, zu der sie gehören:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipClass(Class clazz) { return false; } @Override public boolean shouldSkipField(FieldAttributes field) { return field.getName().startsWith("other"); } }; 

5.3. Mit einer benutzerdefinierten Anmerkung

Ein weiterer intelligenter Ansatz besteht darin, eine benutzerdefinierte Anmerkung zu erstellen:

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

Wir können dann ExclusionStrategy ausnutzen , damit es genau wie mit der @ Expose- Annotation funktioniert , aber umgekehrt:

public class MyClass { private long id; private String name; @Exclude private String other; private MySubClass subclass; } public class MySubClass { private long id; private String description; @Exclude private String otherVerboseInfo; } 

Und hier ist die Strategie:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipClass(Class clazz) { return false; } @Override public boolean shouldSkipField(FieldAttributes field) { return field.getAnnotation(Exclude.class) != null; } }; 

Diese StackOverflow-Antwort beschrieb zuerst diese Technik.

Es ermöglicht uns, die Anmerkung und die Strategie einmal zu schreiben und unsere Felder ohne weitere Änderungen dynamisch zu kommentieren.

5.4. Ausschlussstrategie auf Deserialisierung ausweiten

Unabhängig davon, welche Strategie wir verwenden, können wir immer steuern, wo sie angewendet werden soll.

Nur während der Serialisierung:

Gson gson = new GsonBuilder().addSerializationExclusionStrategy(strategy) 

Nur während der Deserialisierung:

Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(strategy) 

Immer:

Gson gson = new GsonBuilder().setExclusionStrategies(strategy); 

6. Fazit

Wir haben verschiedene Möglichkeiten gesehen, Felder während der Gson-Serialisierung von einer Klasse und ihren Unterklassen auszuschließen.

Wir haben auch die wichtigsten Vorteile und Fallstricke jeder Lösung untersucht.

Wie immer ist der vollständige Quellcode auf Github verfügbar.