Sluit velden uit van serialisatie in Gson

1. Overzicht

In deze korte tutorial gaan we de beschikbare opties onderzoeken om een ​​of meer velden van een Java-klasse en zijn subklassen uit te sluiten van Gson-serialisering.

2. Eerste installatie

Laten we eerst onze klassen definiëren:

@Data @AllArgsConstructor openbare klasse MyClass {privé lange id; private String naam; privé String andere; privé MySubClass-subklasse; } @Data @AllArgsConstructor openbare klasse MySubClass {privé lange id; private String beschrijving; private String otherVerboseInfo; } 

We hebben ze voor het gemak geannoteerd met Lombok (syntactische suiker voor getters, setters, constructeurs…).

Laten we ze nu vullen:

MySubClass-subklasse = nieuwe MySubClass (42L, "het antwoord", "Uitgebreid veld om niet te serialiseren") MyClass source = nieuwe MyClass (1L, "foo", "bar", subklasse); 

Ons doel is om de MyClass.other en MySubClass.otherVerboseInfo velden worden niet geserialiseerd.

De output die we verwachten te krijgen is:

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

In Java:

String verwachteResult = "{\" id \ ": 1, \" naam \ ": \" foo \ ", \" subklasse \ ": {\" id \ ": 42, \" description \ ": \" het antwoord \ "}}"; 

3. Tijdelijke modificator

We kunnen een veld markeren met de voorbijgaand modificator:

openbare klasse MyClass {privé lange id; private String naam; privé voorbijgaande String andere; privé MySubClass-subklasse; } openbare klasse MySubClass {lange privé-id; private String beschrijving; private transient String otherVerboseInfo; } 

Gson-serialisator negeert elk veld dat als tijdelijk is gedeclareerd:

String jsonString = new Gson (). ToJson (source); assertEquals (verwachtResultaat, jsonString); 

Hoewel dit erg snel is, heeft het ook een ernstig nadeel: elke serialisatie-tool houdt rekening met voorbijgaande aard, niet alleen Gson.

Voorbijgaand is de Java-manier om uit te sluiten van serialisatie, dan wordt ons veld ook uitgefilterd door Serialiseerbaar'S serialisatie, en door elk bibliotheektool of framework dat onze objecten beheert.

Bovendien is het voorbijgaand trefwoord werkt altijd voor zowel serialisatie als deserialisatie, wat beperkend kan zijn, afhankelijk van de use-cases.

4. @Expose Annotatie

Gson com.google.gson.annotaties @Expose annotatie werkt andersom.

We kunnen het gebruiken om aan te geven welke velden moeten worden geserialiseerd en de andere negeren:

openbare klasse MyClass {@Expose privé lange id; @Expose private String naam; privé String andere; @Expose privé MySubClass-subklasse; } openbare klasse MySubClass {@Expose privé lange id; @Expose private String beschrijving; private String otherVerboseInfo; } 

Hiervoor moeten we Gson instantiëren met een GsonBuilder:

Gson gson = nieuwe GsonBuilder () .excludeFieldsWithoutExposeAnnotation () .create (); String jsonString = gson.toJson (broncode); assertEquals (verwachtResultaat, jsonString); 

Deze keer kunnen we op veldniveau bepalen of het filteren moet plaatsvinden voor serialisatie, deserialisatie of beide (standaard).

Laten we eens kijken hoe we dit kunnen voorkomen MyClass.other niet worden geserialiseerd, maar toestaan ​​dat het wordt gevuld tijdens een deserialisering vanuit JSON:

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

Hoewel dit de gemakkelijkste manier is die door Gson wordt geboden, en het geen invloed heeft op andere bibliotheken, kan het redundantie in de code impliceren. Als we een klasse hebben met honderd velden, en we willen maar één veld uitsluiten, dan moeten we negenennegentig annotaties schrijven, wat overdreven is.

5. ExclusionStrategy

Een zeer aanpasbare oplossing is het gebruik van een com.google.gson.ExclusionStrategy.

Het stelt ons in staat om (extern of met een Anonieme Innerlijke Klasse) een strategie te definiëren om de GsonBuilder te instrueren of velden (en / of klassen) moeten worden geserialiseerd met aangepaste criteria.

Gson gson = nieuwe GsonBuilder () .addSerializationExclusionStrategy (strategie) .create (); String jsonString = gson.toJson (broncode); assertEquals (verwachtResultaat, jsonString); 

Laten we eens kijken naar enkele voorbeelden van slimme strategieën die u kunt gebruiken.

5.1. Met klassen en veldnamen

Natuurlijk kunnen we ook een of meer velden / klassenamen hardcoderen:

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 openbare boolean shouldSkipClass (Class clazz) {return false; }}; 

Dit is snel en direct ter zake, maar niet erg herbruikbaar en ook vatbaar voor fouten als we onze attributen hernoemen.

5.2. Met zakelijke criteria

Omdat we gewoon een booleaanse waarde moeten retourneren, kunnen we elke bedrijfslogica die we leuk vinden binnen die methode implementeren.

In het volgende voorbeeld zullen we elk veld dat begint met "other" identificeren als velden die niet geserialiseerd mogen worden, ongeacht de klasse waartoe ze behoren:

ExclusionStrategy-strategie = nieuwe ExclusionStrategy () {@Override openbare boolean shouldSkipClass (Class clazz) {return false; } @Override openbare boolean shouldSkipField (FieldAttributes-veld) {return field.getName (). StartsWith ("other"); }}; 

5.3. Met een aangepaste annotatie

Een andere slimme aanpak is om een ​​aangepaste annotatie te maken:

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

We kunnen dan exploiteren Uitsluitingsstrategie om het precies zo te laten werken als bij de @Expose annotatie, maar omgekeerd:

openbare klasse MyClass {privé lange id; private String naam; @Exclude private String other; privé MySubClass-subklasse; } openbare klasse MySubClass {lange privé-id; private String beschrijving; @Exclude private String otherVerboseInfo; } 

En hier is de strategie:

ExclusionStrategy-strategie = nieuwe ExclusionStrategy () {@Override openbare boolean shouldSkipClass (Class clazz) {return false; } @Override openbare boolean shouldSkipField (FieldAttributes-veld) {return field.getAnnotation (Exclude.class)! = Null; }}; 

Dit StackOverflow-antwoord beschreef eerst deze techniek.

Het stelt ons in staat om de annotatie en de Strategie één keer te schrijven en om onze velden dynamisch te annoteren zonder verdere wijziging.

5.4. Breid de uitsluitingsstrategie uit tot deserialisatie

Welke strategie we ook gebruiken, we kunnen altijd bepalen waar deze moet worden toegepast.

Alleen tijdens serialisering:

Gson gson = nieuwe GsonBuilder (). AddSerializationExclusionStrategy (strategie) 

Alleen tijdens deserialisatie:

Gson gson = nieuwe GsonBuilder (). AddDeserializationExclusionStrategy (strategie) 

Altijd:

Gson gson = nieuwe GsonBuilder (). SetExclusionStrategies (strategie); 

6. Conclusie

We hebben verschillende manieren gezien om velden uit te sluiten van een klasse en zijn subklassen tijdens Gson-serialisering.

We hebben ook de belangrijkste voordelen en valkuilen van elke oplossing onderzocht.

Zoals altijd is de volledige broncode beschikbaar op Github.