Een lijst serialiseren en deserialiseren met Gson

1. Inleiding

In deze zelfstudie onderzoeken we enkele geavanceerde gevallen van serialisatie en deserialisering voor Lijst met behulp van de Gson-bibliotheek van Google.

2. Lijst met objecten

Een veelvoorkomend gebruik is het serialiseren en deserialiseren van een lijst met POJO's.

Overweeg de klas:

openbare klasse MyClass {privé int id; private String naam; openbare MyClass (int id, String naam) {this.id = id; this.name = naam; } // getters en setters}

Hier is hoe we zouden serialiseren Lijst:

@Test openbare ongeldig gegevenListOfMyClass_whenSerializing_thenCorrect () {List list = Arrays.asList (nieuwe MyClass (1, "name1"), nieuwe MyClass (2, "name2")); Gson gson = nieuwe Gson (); String jsonString = gson.toJson (lijst); String verwachteString = "[{\" id \ ": 1, \" naam \ ": \" naam1 \ "}, {\" id \ ": 2, \" naam \ ": \" naam2 \ "}]" ; assertEquals (verwachteString, jsonString); }

Zoals we kunnen zien, is serialisatie redelijk eenvoudig.

Deserialisatie is echter lastig. Hier is een verkeerde manier om het te doen:

@Test (verwacht = ClassCastException.class) openbare ongeldige gegeven JsonString_whenIncorrectDeserializing_thenThrowClassCastException () {String inputString = "[{\" id \ ": 1, \" naam \ ": \" naam1 \ "}, {\" id \ ": 2 , \ "naam \": \ "naam2 \"}] "; Gson gson = nieuwe Gson (); Lijst outputList = gson.fromJson (inputString, ArrayList.class); assertEquals (1, outputList.get (0) .getId ()); }

Hier, hoewel we een Lijst van maat twee, na deserialisatie, zou het geen Lijst van Mijn klas. Daarom gooit regel # 6 ClassCastException.

Gson kan een verzameling willekeurige objecten serialiseren, maar kan de gegevens niet deserialiseren zonder aanvullende informatie. Dat komt omdat de gebruiker het type van het resulterende object niet kan aangeven. In plaats daarvan wordt de Verzameling moet van een specifiek, algemeen type zijn.

De juiste manier om het Lijst zou zijn:

@Test openbare leegte gegevenJsonString_whenDeserializing_thenReturnListOfMyClass () {String inputString = "[{\" id \ ": 1, \" naam \ ": \" naam1 \ "}, {\" id \ ": 2, \" naam \ ": \ "naam2 \"}] "; Lijst inputList = Arrays.asList (nieuwe MyClass (1, "name1"), nieuwe MyClass (2, "name2")); Typ listOfMyClassObject = nieuw TypeToken() {} .getType (); Gson gson = nieuwe Gson (); Lijst outputList = gson.fromJson (inputString, listOfMyClassObject); assertEquals (inputList, outputList); }

Hier, we gebruiken Gson's TypeToken om het juiste type te bepalen dat gedeserialiseerd moet worden - ArrayList. Het idioom dat werd gebruikt om de listOfMyClassObject definieert eigenlijk een anonieme lokale innerlijke klasse die een methode bevat getType () dat geeft het volledig geparametriseerde type terug.

3. Lijst met polymorfe objecten

3.1. Het probleem

Beschouw een voorbeeld van een klassenhiërarchie van dieren:

openbare abstracte klasse Dier {// ...} openbare klasse Hond breidt Dier uit {// ...} openbare klasse Koe breidt Dier uit {// ...}

Hoe serialiseren en deserialiseren we Lijst? We kunnen gebruiken TypeToken zoals we in de vorige sectie hebben gebruikt. Gson kan echter nog steeds niet het concrete gegevenstype achterhalen van de objecten die in de lijst zijn opgeslagen.

3.2. Custom Deserializer gebruiken

Een manier om dit op te lossen, is door type-informatie toe te voegen aan de geserialiseerde JSON. We respecteren dat type informatie tijdens JSON-deserialisatie. Hiervoor moeten we onze eigen aangepaste serialisator en deserializer schrijven.

Ten eerste introduceren we een nieuw Draad veld genaamd type in de basisklasse Dier. Het slaat de eenvoudige naam op van de klasse waartoe het behoort.

Laten we eens kijken naar onze voorbeeldlessen:

openbare abstracte klasse Animal {public String type = "Animal"; }
public class Dog breidt Animal {private String petName; openbare hond () {petName = "Milo"; type = "Hond"; } // getters en setters}
openbare klasse Koe breidt Dier uit {privé String-ras; openbare koe () {breed = "Jersey"; type = "Koe"; } // getters en setters}

Serialisatie blijft zonder problemen werken zoals voorheen:

@Test openbare ongeldig gegeven PolymorphicList_whenSerializeWithTypeAdapter_thenCorrect () {String verwachteString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Hond \ "}, {\" ras \ ": \" Jersey \ ", \" type \ ": \" Koe \ "}]"; List inList = nieuwe ArrayList (); inList.add (new Dog ()); inList.add (nieuwe koe ()); String jsonString = nieuwe Gson (). ToJson (inList); assertEquals (verwachteString, jsonString); }

Om de lijst te deserialiseren, moeten we een aangepaste deserializer leveren:

openbare klasse AnimalDeserializer implementeert JsonDeserializer {private String animalTypeElementName; privé Gson gson; privékaart animalTypeRegistry; openbare AnimalDeserializer (String animalTypeElementName) {this.animalTypeElementName = animalTypeElementName; this.gson = nieuwe Gson (); this.animalTypeRegistry = nieuwe HashMap (); } public void registerBarnType (String animalTypeName, Class animalType) {animalTypeRegistry.put (animalTypeName, animalType); } openbare Animal deserialize (JsonElement json, Type typeOfT, JsonDeserializationContext context) {JsonObject animalObject = json.getAsJsonObject (); JsonElement animalTypeElement = animalObject.get (animalTypeElementName); Klasse animalType = animalTypeRegistry.get (animalTypeElement.getAsString ()); retourneer gson.fromJson (animalObject, animalType); }}

Hier de animalTypeRegistry map onderhoudt de toewijzing tussen de klassenaam en het klassetype.

Tijdens deserialisatie halen we eerst het nieuw toegevoegde type veld. Met behulp van deze waarde zoeken we de animalTypeRegistry map om het concrete gegevenstype te krijgen. Dit gegevenstype wordt vervolgens doorgegeven aan van Json ().

Laten we eens kijken hoe we onze aangepaste deserializer kunnen gebruiken:

@Test openbare ongeldig gegeven PolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Hond \ "}, {\" breed \ ": \" Jersey \ ", \" type \ ": \" Koe \ "}]"; AnimalDeserializer deserializer = nieuwe AnimalDeserializer ("type"); deserializer.registerBarnType ("Dog", Dog.class); deserializer.registerBarnType ("Cow", Cow.class); Gson gson = nieuwe GsonBuilder () .registerTypeAdapter (Animal.class, deserializer) .create (); Lijst outList = gson.fromJson (inputString, nieuw TypeToken() {}. getType ()); assertEquals (2, outList.size ()); assertTrue (outList.get (0) instanceof Dog); assertTrue (outList.get (1) instanceof Cow); }

3.3. Gebruik makend van RuntimeTypeAdapterFactory

Een alternatief voor het schrijven van een aangepaste deserializer is om de RuntimeTypeAdapterFactory klasse aanwezig in de Gson-broncode. Echter, het wordt niet weergegeven door de bibliotheek zodat de gebruiker het kan gebruiken. Daarom moeten we een kopie van de klasse maken in ons Java-project.

Zodra dit is gebeurd, kunnen we het gebruiken om onze lijst te deserialiseren:

@Test openbare ongeldig gegeven PolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Hond \ "}, {\" breed \ ": \" Jersey \ ", \" type \ ": \" Koe \ "}]"; Typ listOfAnimals = nieuw TypeToken() {}. getType (); RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of (Animal.class, "type") .registerSubtype (Dog.class) .registerSubtype (Cow.class); Gson gson = nieuwe GsonBuilder (). RegisterTypeAdapterFactory (adapter) .create (); Lijst outList = gson.fromJson (inputString, listOfAnimals); assertEquals (2, outList.size ()); assertTrue (outList.get (0) instanceof Dog); assertTrue (outList.get (1) instanceof Cow); }

Merk op dat het onderliggende mechanisme nog steeds hetzelfde is.

We moeten nog steeds de type-informatie invoeren tijdens de serialisering. De type-informatie kan later worden gebruikt tijdens deserialisatie. Vandaar het veld type is nog steeds vereist in elke klas om deze oplossing te laten werken. We hoeven gewoon niet onze eigen deserializer te schrijven.

RuntimeTypeAdapterFactory biedt het juiste type adapter op basis van de veldnaam die eraan is doorgegeven en de geregistreerde subtypen.

4. Conclusie

In dit artikel hebben we gezien hoe je een lijst met objecten kunt serialiseren en deserialiseren met Gson.

Zoals gewoonlijk is de code beschikbaar op GitHub.