"Salut amice!"

— Hei, Diego.

„Văd că aici ați învățat elementele de bază ale serializării JSON?”

"Ce vrei să spui "de bază"? Știu multe!"

„Atât de naiv. Nu știi jumătate. În cel mai bun caz, zece la sută”.

"Glumești. Ce mai este?"

"Deserializarea unei ierarhii de obiecte (deserializarea polimorfă), deserializarea colecțiilor și multe altele! Cadrul Jackson este mare și puternic. Sincer, ai început abia să zgârie suprafața."

„Bine, atunci spune-mi despre asta – sunt toată urechile”.

„Îmi face plăcere să devin mai deștept cu fiecare lecție!”

— Ei bine, îmi face plăcere să ajut, prietene robot!

"Ești gata? Atunci ascultă."

„După cum ați învățat deja, adnotările sunt folosite atât pentru serializare, cât și pentru deserializare. În practică, serializarea necesită mult mai puține informații decât deserializarea. De exemplu:”

Clasa Java JSON
class Cat
{
 public String name = "missy";
 public Cat[] cats = new Cat[0];
}
{
 "name": "missy",
 "cats": []
}
class Cat
{
 public String name = "missy";
 public List cats = new ArrayList<Cat>();
}
{
 "name": "missy",
 "cats": []
}
class Cat
{
 public String name = "missy";
 public List cats = new LinkedList<Cat>();
}
{
 "name": "missy",
 "cats": []
}

„Instanțele Array, ArrayList, LinkedList și alte clase sunt transformate în matrice JSON.”

„Dar când deserializezi o matrice JSON, ce obiect ar trebui să creezi: un ArrayList sau un LinkedList?”

"Corect. Dacă un membru al clasei este o interfață (de ex. Public List<Cat> cats ), ce obiect ar trebui să-i fie atribuit?"

„Putem adăuga adnotări suplimentare la câmp sau putem indica în mod explicit clasele țintă în timpul deserializării. Priviți acest exemplu:”

Convertiți un obiect din JSON
public static void main(String[] args) throws IOException
{
 String jsonString = ""{\"name\":\"Missy\",\"cats\":[{\"name\":\"Timmy\"},{\"name\":\"Killer\"}]}"";
 StringReader reader = new StringReader(jsonString);
 ObjectMapper mapper = new ObjectMapper();
 Cat cat = mapper.readValue(reader, TypeFactory.collectionType(ArrayList.class, Cat.class));
}
O clasă ale cărei obiecte sunt deserializate din JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

„Cu alte cuvinte, putem folosi al doilea parametru al metodei mapper . readValue pentru a trece lista de clase de utilizat în timpul deserializării.”

„Îmi place. Este convenabil. Deci, puteți deserializa o matrice JSON în orice aveți nevoie, o ArrayList sau o LinkedList.

"Ai menționat și folosirea adnotărilor. Cum faci asta?"

"Este ușor. De exemplu:"

Convertiți un obiect din JSON
public static void main(String[] args) throws IOException
{
 String jsonString = ""{\"name\":\"Missy\",\"cats\":[{\"name\":\"Timmy\"},{\"name\":\"Killer\"}]}"";
 StringReader reader = new StringReader(jsonString);

 ObjectMapper mapper = new ObjectMapper();

 Cat cat = mapper.readValue(reader, Cat.class);
}
O clasă ale cărei obiecte sunt deserializate din JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

„Adăugăm pur și simplu adnotarea @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) la linia 5 pentru a indica ce implementare a interfeței List să folosim.”

"Ah. Înțeleg. Asta chiar este destul de simplu."

"Dar mai sunt. Să presupunem că tipul de date dintr-o Listă este și o interfață! Ce ai face?"

— Folosim și aici o adnotare?

"Da, același. Puteți să îl utilizați și pentru a indica tipul de parametru. Astfel:"

Tipul colectiei Cum să setați tipul de date
Listă @JsonDeserialize(contentAs = ValueTypeImpl.class)
Hartă @JsonDeserialize(keyAs = KeyTypeImpl.class)

"Mis! Sunt într-adevăr o mulțime de adnotări necesare pentru diverse situații pe care nu le putem anticipa."

„Asta nu este tot. Și asta ne aduce la cursul principal: în proiectele reale, clasele moștenesc destul de des aceeași clasă de bază sau interfață, care este folosită practic peste tot. Și acum imaginați-vă că trebuie să deserializați o structură de date care conține astfel de clase. De exemplu:"

Convertiți un obiect în JSON
public static void main(String[] args) throws IOException
{
 Cat cat = new Cat();
 cat.name = "Missy";
 cat.age = 5;

 Dog dog = new Dog();
 dog.name = "Killer";
 dog.age = 8;
 dog.owner = "Bill Jefferson";

 ArrayList<Pet> pets = new ArrayList<Pet>();
 pets.add(cat);
 pets.add(dog);

 StringWriter writer = new StringWriter();
 ObjectMapper mapper = new ObjectMapper();
 mapper.writeValue(writer, pets);
 System.out.println(writer.toString());
}
O clasă ale cărei obiecte se convertesc în JSON
@JsonAutoDetect
class Pet
{
 public String name;
}

@JsonAutoDetect
class Cat extends Pet
{
 public int age;
}

@JsonAutoDetect
class Dog extends Pet
{
 public int age;
 public String owner;
}
Rezultatul serializării și rezultatul ecranului:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

„Fii atent la rezultatul serializării”.

„Nu putem deserializa aceste date într-un obiect Java, deoarece în esență nu se pot distinge de datele pentru alte clase.”

„Există câteva trăsături distinctive: Câinele are un câmp de proprietar.”

„Da, dar acest câmp ar putea fi nul sau ar putea fi omis complet în timpul serializării.”

„Ei bine, nu putem specifica tipul de date folosind adnotări pe care le cunoaștem?”

"Nu. După deserializare, o singură colecție ar trebui să aibă diverse obiecte Cat și Dog, precum și alte duzini de clase care ar putea moșteni de la Pet."

— Ce naiba poţi face aici?

— Aici se folosesc două lucruri.

"În primul rând, un anumit câmp este ales pentru a distinge un tip de altul. Dacă nu există unul, atunci este creat."

„În al doilea rând, există adnotări speciale care vă permit să controlați procesul de „deserializare polimorfă”. Iată ce puteți face:”

Convertiți un obiect în JSON
public static void main(String[] args) throws IOException
{
 Cat cat = new Cat();
 cat.name = "Missy";
 cat.age = 5;

 Dog dog = new Dog();
 dog.name = "Killer";
 dog.age = 8;
 dog.owner = "Bill Jeferson";

 House house = new House();
 house.pets.add(dog);
 house.pets.add(cat);

 StringWriter writer = new StringWriter();
 ObjectMapper mapper = new ObjectMapper();
 mapper.writeValue(writer, house);
 System.out.println(writer.toString());
}
O clasă ale cărei obiecte se convertesc în JSON
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Cat.class, name = "cat"),
@JsonSubTypes.Type(value = Dog.class, name = "dog")
})
class Pet
{
 public String name;
}

class Cat extends Pet
{
 public int age;
}

class Dog extends Pet
{
 public int age;
 public String owner;
}

class House
{
 public List&ltPet> pets = new ArrayList<>();
}
Rezultatul serializării și rezultatul ecranului:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Folosind adnotări, indicăm că reprezentarea JSON va conține un câmp special numit tip care va deține valoarea cat pentru clasa Cat și valoarea câine pentru clasa Dog . Aceste informații sunt suficiente pentru a deserializa corect un obiect: în timpul deserializării, tipul obiectului care urmează să fie creat va fi determinat de valoarea câmpului de tip.

„Uneori, numele clasei este folosit ca valoare a câmpului de tip (de exemplu, «com.example.entity.Cat.class»), dar aceasta nu este o practică bună. Cum ar ști o aplicație externă care primește JSON-ul nostru numele clasele noastre? Mai rău, clasele sunt uneori redenumite. Este mai bine să folosiți un nume unic pentru a identifica o anumită clasă."

"Mis! Suspin. Nu mi-am dat seama că deserializarea este atât de complicată. Și că există atât de multe pe care le poți ajusta."

"Da. Acestea sunt concepte noi pentru tine, dar acesta este genul de cunoștințe practice care te va face un programator de geniu."

"Amigo este un programator tare. Tare!"

"OK. Du-te și ia o pauză."