"Ciao amico!"

"Ciao Diego."

"Vedo qui che hai imparato le basi della serializzazione JSON?"

"Cosa intendi per 'elementi di base'? So molte cose!"

"Così ingenuo. Non ne conosci la metà. Il dieci percento al massimo."

"Stai scherzando. Cos'altro c'è?"

"Deserializzazione di una gerarchia di oggetti (deserializzazione polimorfica), deserializzazione di raccolte e molto altro ancora! Il framework Jackson è ampio e potente. Onestamente, hai solo iniziato a grattare la superficie."

"Va bene, allora raccontamelo – sono tutt'orecchi."

"Mi piace davvero diventare più intelligente a ogni lezione!"

"Beh, è ​​un piacere aiutarti, mio ​​amico robot!"

"Sei pronto? Allora ascolta."

"Come hai già imparato, le annotazioni vengono utilizzate sia per la serializzazione che per la deserializzazione. In pratica, la serializzazione richiede molte meno informazioni rispetto alla deserializzazione. Ad esempio:"

Classe 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": []
}

"Le istanze di Array, ArrayList, LinkedList e altre classi vengono trasformate in array JSON."

"Ma quando deserializzi un array JSON, quale oggetto dovresti creare: un ArrayList o un LinkedList?"

"Giusto. Se un membro della classe è un'interfaccia (ad es. public List<Cat> cats ), quale oggetto dovrebbe essere attribuito a esso?"

"Possiamo aggiungere ulteriori annotazioni al campo o indicare esplicitamente le classi di destinazione durante la deserializzazione. Guarda questo esempio:"

Converti un oggetto da 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));
}
Una classe i cui oggetti sono deserializzati da JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"In altre parole, possiamo utilizzare il secondo parametro del metodo mapper . readValue per passare l'elenco delle classi da utilizzare durante la deserializzazione."

"Mi piace. È conveniente. Quindi puoi deserializzare un array JSON in qualsiasi cosa ti serva, un ArrayList o un LinkedList.

"Hai anche accennato all'uso delle annotazioni. Come si fa?"

"È facile. Ad esempio:"

Converti un oggetto da 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);
}
Una classe i cui oggetti sono deserializzati da JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Aggiungiamo semplicemente l'annotazione @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) alla riga 5 per indicare quale implementazione dell'interfaccia List utilizzare."

"Ah. Capisco. È molto semplice."

"Ma c'è di più. Supponiamo che il tipo di dati in una lista sia anche un'interfaccia! Cosa faresti?"

"Usiamo un'annotazione anche qui?"

"Sì, lo stesso. Puoi usarlo anche per indicare il tipo di parametro. In questo modo:"

Tipo di raccolta Come impostare il tipo di dati
Elenco @JsonDeserialize(contentAs = ValueTypeImpl.class)
Carta geografica @JsonDeserialize(keyAs = KeyTypeImpl.class)

"Fantastico! Ci sono davvero molte annotazioni necessarie per varie situazioni che non possiamo prevedere."

"Non è tutto. E questo ci porta al corso principale: nei progetti reali, le classi ereditano molto spesso la stessa classe o interfaccia di base, che viene utilizzata praticamente ovunque. E ora immagina di dover deserializzare una struttura dati contenente tali classi. Per esempio:"

Converti un oggetto in 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());
}
Una classe i cui oggetti vengono convertiti in 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;
}
Risultato della serializzazione e output dello schermo:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

"Presta attenzione al risultato della serializzazione."

"Non possiamo deserializzare questi dati in un oggetto Java, poiché è essenzialmente indistinguibile dai dati per altre classi."

"Ci sono alcune caratteristiche distintive: il cane ha un campo proprietario."

"Sì, ma questo campo potrebbe essere nullo o potrebbe essere completamente saltato durante la serializzazione."

"Bene, non possiamo specificare il tipo di dati utilizzando annotazioni che conosciamo?"

"No. Dopo la deserializzazione, una singola raccolta dovrebbe contenere vari oggetti Cat e Dog, oltre a una dozzina di altre classi che potrebbero ereditare da Pet."

"Cosa diavolo puoi fare qui?"

"Qui si usano due cose."

"In primo luogo, viene scelto un determinato campo per distinguere un tipo da un altro. Se non ce n'è uno, allora viene creato."

"In secondo luogo, ci sono annotazioni speciali che ti consentono di controllare il processo di «deserializzazione polimorfica». Ecco cosa puoi fare:"

Converti un oggetto in 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());
}
Una classe i cui oggetti vengono convertiti in 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<>();
}
Risultato della serializzazione e output dello schermo:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Utilizzando le annotazioni, indichiamo che la rappresentazione JSON conterrà un campo speciale chiamato type che conterrà il valore cat per la classe Cat e il valore dog per la classe Dog . Questa informazione è sufficiente per deserializzare correttamente un oggetto: durante la deserializzazione, il tipo dell'oggetto da creare sarà determinato dal valore del campo type.

"A volte il nome della classe viene utilizzato come valore del campo del tipo (ad es. «com.example.entity.Cat.class»), ma questa non è una buona pratica. In che modo un'applicazione esterna che riceve il nostro JSON conosce i nomi di le nostre classi? Peggio ancora, le classi a volte vengono rinominate. È meglio usare un nome univoco per identificare una classe specifica."

"Fantastico! Sigh. Non mi ero reso conto che la deserializzazione fosse così complicata. E che c'è così tanto che puoi mettere a punto."

"Sì. Questi sono nuovi concetti per te, ma questo è il tipo di conoscenza pratica che ti renderà un geniale programmatore."

"Amigo è un programmatore fantastico. Fantastico!"

"OK. Vai e prenditi una pausa."