Serialisering og deserialisering af en liste med Gson

1. Introduktion

I denne vejledning udforsker vi et par avancerede tilfælde til serialisering og deserialisering til Liste ved hjælp af Googles Gson-bibliotek.

2. Liste over objekter

En almindelig brugssag er at serieisere og deserialisere en liste over POJO'er.

Overvej klassen:

offentlig klasse MyClass {privat int id; privat strengnavn; public MyClass (int id, String name) {this.id = id; dette.navn = navn; } // getters og setters}

Sådan serier vi Liste:

@Test offentlig ugyldighed givenListOfMyClass_whenSerializing_thenCorrect () {List list = Arrays.asList (new MyClass (1, "name1"), new MyClass (2, "name2")); Gson gson = ny Gson (); String jsonString = gson.toJson (liste); Streng forventetString = "[{\" id \ ": 1, \" navn \ ": \" navn1 \ "}, {\" id \ ": 2, \" navn \ ": \" navn2 \ "}]" ; assertEquals (expectString, jsonString); }

Som vi kan se, er serialisering ret ligetil.

Deserialisering er imidlertid vanskelig. Her er en forkert måde at gøre det på:

@Test (forventet = ClassCastException.class) offentlig ugyldighed givetJsonString_whenIncorrectDeserializing_thenThrowClassCastException () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2 , \ "navn \": \ "navn2 \"}] "; Gson gson = ny Gson (); Liste outputList = gson.fromJson (inputString, ArrayList.class); assertEquals (1, outputList.get (0) .getId ()); }

Her, selvom vi ville få en Liste af størrelse to, post-deserialisering, ville det ikke være en Liste af Min klasse. Derfor kaster linie 6 ClassCastException.

Gson kan serieisere en samling af vilkårlige objekter, men kan ikke deserialisere dataene uden yderligere information. Det er fordi der ikke er nogen måde for brugeren at angive typen af ​​det resulterende objekt. I stedet, mens deserialisering, Kollektion skal være af en bestemt generisk type.

Den korrekte måde at deserialisere Liste ville være:

@Test offentligt ugyldigt givetJsonString_whenDeserializing_thenReturnListOfMyClass () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2, \" name \ ": \ "navn2 \"}] "; Liste inputList = Arrays.asList (ny MyClass (1, "name1"), ny MyClass (2, "name2")); Type listOfMyClassObject = ny TypeToken() {} .getType (); Gson gson = ny Gson (); Liste outputList = gson.fromJson (inputString, listOfMyClassObject); assertEquals (inputList, outputList); }

Her, vi bruger Gson's TypeToken for at bestemme den rigtige type, der skal deserialiseres - ArrayList. Idiomet bruges til at få listOfMyClassObject definerer faktisk en anonym lokal indre klasse indeholdende en metode getType () der returnerer den fuldt parametriserede type.

3. Liste over polymorfe objekter

3.1. Problemet

Overvej et eksempel på klassehierarki af dyr:

offentlig abstrakt klasse Dyr {// ...} offentlig klasse Hund udvider Dyr {// ...} offentlig klasse Ko udvider dyr {// ...}

Hvordan serieres og deserialiseres vi Liste? Vi kunne bruge TypeToken som vi brugte i det foregående afsnit. Gson kan dog stadig ikke finde ud af den konkrete datatype for de objekter, der er gemt på listen.

3.2. Brug af brugerdefineret deserializer

En måde at løse dette på er at tilføje typeoplysninger til den serielle JSON. Vi respekterer den type information under JSON-deserialisering. Til dette er vi nødt til at skrive vores egen tilpassede serializer og deserializer.

For det første introducerer vi et nyt Snor felt kaldes type i basisklassen Dyr. Det gemmer det enkle navn på den klasse, det tilhører.

Lad os se på vores prøveklasser:

offentlig abstrakt klasse Animal {public String type = "Animal"; }
offentlig klasse Hund udvider Animal {private String petName; offentlig hund () {petName = "Milo"; type = "Hund"; } // getters og setters}
offentlig klasse Ko udvider dyr {privat streng race; offentlig ko () {breed = "Jersey"; type = "ko"; } // getters og setters}

Serialisering fungerer fortsat som før uden problemer:

@Test offentligt ugyldigt givetPolymorphicList_whenSerializeWithTypeAdapter_thenCorrect () {String expectString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" race \ ": \" Jersey \ ", \" type \ ": \" Ko \ "}]"; Liste inList = ny ArrayList (); inList.add (ny hund ()); inList.add (ny ko ()); String jsonString = ny Gson (). ToJson (inList); assertEquals (expectString, jsonString); }

For at deserialisere listen skal vi levere en brugerdefineret deserializer:

offentlig klasse AnimalDeserializer implementerer JsonDeserializer {private String animalTypeElementName; privat Gson gson; privat kort animalTypeRegistry; public AnimalDeserializer (String animalTypeElementName) {this.animalTypeElementName = animalTypeElementName; this.gson = ny Gson (); this.animalTypeRegistry = ny HashMap (); } public void registerBarnType (String animalTypeName, Class animalType) {animalTypeRegistry.put (animalTypeName, animalType); } public Animal deserialize (JsonElement json, Type typeOfT, JsonDeserializationContext context) {JsonObject animalObject = json.getAsJsonObject (); JsonElement animalTypeElement = animalObject.get (animalTypeElementName); Klasse animalType = animalTypeRegistry.get (animalTypeElement.getAsString ()); returner gson.fromJson (animalObject, animalType); }}

Her, den animalTypeRegistry map opretholder kortlægningen mellem klassens navn og klassetypen.

Under deserialisering udtrækker vi først det nyligt tilføjede type Mark. Ved hjælp af denne værdi kigger vi på animalTypeRegistry kort for at få den konkrete datatype. Denne datatype videregives derefter til fraJson ().

Lad os se, hvordan vi bruger vores brugerdefinerede deserializer:

@Test offentligt ugyldigt givetPolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" race \ ": \" Jersey \ ", \" type \ ": \" Ko \ "}]"; AnimalDeserializer deserializer = ny AnimalDeserializer ("type"); deserializer.registerBarnType ("Hund", Hund.klasse); deserializer.registerBarnType ("ko", ko.klasse); Gson gson = ny GsonBuilder () .registerTypeAdapter (Animal.class, deserializer) .create (); Liste outList = gson.fromJson (inputString, ny TypeToken() {}. getType ()); assertEquals (2, outList.size ()); assertTrue (outList.get (0) forekomst af hund); assertTrue (outList.get (1) forekomst af ko); }

3.3. Ved brug af RuntimeTypeAdapterFactory

Et alternativ til at skrive en brugerdefineret deserializer er at bruge RuntimeTypeAdapterFactory klasse til stede i Gson-kildekoden. Imidlertid, det udsættes ikke af biblioteket, som brugeren kan bruge. Derfor bliver vi nødt til at oprette en kopi af klassen i vores Java-projekt.

Når dette er gjort, kan vi bruge det til at deserialisere vores liste:

@Test offentligt ugyldigt givetPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" race \ ": \" Jersey \ ", \" type \ ": \" Ko \ "}]"; Type listOfAnimals = nyt TypeToken() {}. getType (); RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of (Animal.class, "type") .registerSubtype (Dog.class) .registerSubtype (Cow.class); Gson gson = ny GsonBuilder (). RegisterTypeAdapterFactory (adapter) .create (); List outList = gson.fromJson (inputString, listOfAnimals); assertEquals (2, outList.size ()); assertTrue (outList.get (0) forekomst af hund); assertTrue (outList.get (1) forekomst af ko); }

Bemærk, at den underliggende mekanisme stadig er den samme.

Vi har stadig brug for at introducere typeoplysningerne under serialisering. Typeoplysningerne kan senere bruges under deserialisering. Derfor marken type kræves stadig i hver klasse for at denne løsning kan fungere. Vi behøver bare ikke at skrive vores egen deserializer.

RuntimeTypeAdapterFactory giver den korrekte type adapter baseret på feltnavnet, der sendes til det, og de registrerede undertyper.

4. Konklusion

I denne artikel så vi, hvordan vi serierer og deserialiserer en liste over objekter ved hjælp af Gson.

Som sædvanlig er koden tilgængelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found