Serializácia a deserializácia zoznamu s Gson

1. Úvod

V tomto tutoriáli preskúmame niekoľko pokročilých prípadov serializácie a deserializácie pre Zoznam pomocou knižnice Gson spoločnosti Google.

2. Zoznam objektov

Jedným z bežných prípadov použitia je serializácia a deserializácia zoznamu POJO.

Zvážte triedu:

public class MyClass {private int id; súkromné ​​meno reťazca; public MyClass (int id, String name) {this.id = id; this.name = meno; } // zakladatelia a zakladatelia}

Takto by sme pokračovali Zoznam:

@Test public void givenListOfMyClass_whenSerializing_thenCorrect () {List list = Arrays.asList (new MyClass (1, "name1"), new MyClass (2, "name2")); Gson gson = nový Gson (); Reťazec jsonString = gson.toJson (zoznam); Reťazec expectString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2, \" name \ ": \" name2 \ "}]" " ; assertEquals (expectString, jsonString); }

Ako vidíme, serializácia je dosť jednoduchá.

Deserializácia je však ošemetná. Tu je nesprávny spôsob, ako to urobiť:

@Test (očakáva sa = ClassCastException.class) public void givenJsonString_whenIncorrectDeserializing_thenThrowClassCastException () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2 , \ "name \": \ "name2 \"}] "; Gson gson = nový Gson (); Zoznam outputList = gson.fromJson (inputString, ArrayList.class); assertEquals (1, outputList.get (0) .getId ()); }

Tu, hoci by sme dostali a Zoznam veľkosti dva, post-deserializácia, to by nebolo Zoznam z Moja trieda. Preto sa hodí riadok # 6 ClassCastException.

Gson môže serializovať zbierku ľubovoľných objektov, ale nemôže deserializovať údaje bez ďalších informácií. Je to preto, že používateľ nemôže nijako označiť typ výsledného objektu. Namiesto toho Zbierka musí byť konkrétneho, generického typu.

Správny spôsob deserializácie Zoznam bolo by:

@Test public void givenJsonString_whenDeserializing_thenReturnListOfMyClass () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2, \" name \ ": \ "name2 \"}] "; Zoznam inputList = Arrays.asList (new MyClass (1, "name1"), new MyClass (2, "name2")); Typ listOfMyClassObject = nový TypeToken() {} .getType (); Gson gson = nový Gson (); Zoznam outputList = gson.fromJson (inputString, listOfMyClassObject); assertEquals (inputList, outputList); }

Tu, používame Gson TypeToken určiť správny typ, ktorý sa má deserializovať - ArrayList. Frazém sa používal na získanie listOfMyClassObject v skutočnosti definuje anonymnú miestnu vnútornú triedu obsahujúcu metódu getType () ktorý vráti plne parametrizovaný typ.

3. Zoznam polymorfných objektov

3.1. Problém

Zvážte príklad triednej hierarchie zvierat:

verejná abstraktná trieda Zviera {// ...} verejná trieda Pes rozširuje zviera {// ...} verejná trieda Krava rozširuje zviera {// ...}

Ako serializujeme a deserializujeme Zoznam? Mohli by sme použiť TypeToken ako sme použili v predchádzajúcej časti. Gson však stále nebude schopný zistiť konkrétny údajový typ objektov uložených v zozname.

3.2. Používanie vlastného deserializátora

Jedným zo spôsobov, ako to vyriešiť, je pridať informácie o type do serializovaného JSON. Ctíme si tieto informácie o type počas deserializácie JSON. Na to musíme napísať vlastný prispôsobený serializátor a deserializátor.

Najskôr predstavíme nový String pole volalo typu v základnej triede Zviera. Ukladá jednoduchý názov triedy, do ktorej patrí.

Pozrime sa na naše ukážkové triedy:

verejná abstraktná trieda Zviera {public String type = "Zviera"; }
public class Dog extends Animal {private String petName; public Dog () {petName = "Milo"; type = "Pes"; } // zakladatelia a zakladatelia}
verejná trieda Krava rozširuje Animal {private String plemeno; public Cow () {plemeno = "Jersey"; typ = "krava"; } // zakladatelia a zakladatelia}

Serializácia bude aj naďalej fungovať ako predtým bez akýchkoľvek problémov:

@Test public void givenPolymorphicList_whenSerializeWithTypeAdapter_thenCorrect () {String expectString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Pes \ "}, {\" plemeno \ ": \" Jersey \ ", \" type \ ": \" Krava \ "}]"; Zoznam inList = nový ArrayList (); inList.add (nový pes ()); inList.add (new Cow ()); Reťazec jsonString = nový Gson (). ToJson (inList); assertEquals (expectString, jsonString); }

Aby sme mohli deserializovať zoznam, budeme musieť poskytnúť vlastný deserializátor:

verejná trieda AnimalDeserializer implementuje JsonDeserializer {private String animalTypeElementName; súkromný Gson gson; súkromná mapa animalTypeRegistry; public AnimalDeserializer (String animalTypeElementName) {this.animalTypeElementName = animalTypeElementName; this.gson = nový Gson (); this.animalTypeRegistry = nový HashMap (); } public void registerBarnType (reťazec animalTypeName, trieda animalType) {animalTypeRegistry.put (animalTypeName, animalType); } verejné Zvieratá deserializovať (JsonElement json, typ TypeOfT, JsonDeserializationContext kontext) {JsonObject animalObject = json.getAsJsonObject (); JsonElement animalTypeElement = animalObject.get (animalTypeElementName); Trieda animalType = animalTypeRegistry.get (animalTypeElement.getAsString ()); vrátiť gson.fromJson (animalObject, animalType); }}

Tu je animalTypeRegistry mapa udržiava mapovanie medzi názvom triedy a typom triedy.

Počas deserializácie najskôr extrahujeme novo pridané typu lúka. Pomocou tejto hodnoty urobíme vyhľadávanie na animalTypeRegistry mapa pre získanie konkrétneho dátového typu. Tento typ údajov sa potom odovzdá fromJson ().

Pozrime sa, ako používať náš vlastný deserializátor:

@ Test public void givenPolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Pes \ "}, {\" plemeno \ ": \" Jersey \ ", \" type \ ": \" Krava \ "}]"; AnimalDeserializer deserializer = nový AnimalDeserializer ("typ"); deserializer.registerBarnType ("Pes", Dog.class); deserializer.registerBarnType ("Krava", Cow.class); Gson gson = nový GsonBuilder () .registerTypeAdapter (Animal.class, deserializer) .create (); Zoznam outList = gson.fromJson (inputString, nový TypeToken() {}. getType ()); assertEquals (2, outList.size ()); assertTrue (outList.get (0) instanceof Dog); assertTrue (outList.get (1) instanceof Cow); }

3.3. Použitím RuntimeTypeAdapterFactory

Alternatívou k napísaniu vlastného deserializátora je použitie RuntimeTypeAdapterFactory triedy prítomnej v zdrojovom kóde Gson. Avšak nie je sprístupnená používateľom v knižnici. Preto budeme musieť v našom projekte Java vytvoriť kópiu triedy.

Len čo to urobíme, môžeme ho použiť na deserializáciu nášho zoznamu:

@Test public void givenPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Pes \ "}, {\" plemeno \ ": \" Jersey \ ", \" type \ ": \" Krava \ "}]"; Typ listOfAnimals = nový TypeToken() {}. getType (); RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of (Animal.class, "type") .registerSubtype (Dog.class) .registerSubtype (Cow.class); Gson gson = nový GsonBuilder (). RegisterTypeAdapterFactory (adaptér) .create (); Zoznam outList = gson.fromJson (inputString, listOfAnimals); assertEquals (2, outList.size ()); assertTrue (outList.get (0) instanceof Dog); assertTrue (outList.get (1) instanceof Cow); }

Upozorňujeme, že základný mechanizmus je stále rovnaký.

Počas serializácie musíme ešte predstaviť typové informácie. Informácie o type je možné neskôr použiť počas deserializácie. Preto pole typu je stále potrebné v každej triede, aby toto riešenie fungovalo. Len nemusíme písať vlastný deserializátor.

RuntimeTypeAdapterFactory poskytuje správny typ adaptéra na základe názvu poľa, ktorý sa mu odovzdal, a registrovaných podtypov.

4. Záver

V tomto článku sme videli, ako serializovať a rekonštruovať zoznam objektov pomocou nástroja Gson.

Ako obvykle je kód k dispozícii na GitHub.


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