"Salut mon pote!"

"Salut, Diego."

"Je vois ici que vous avez appris les bases de la sérialisation JSON ?"

« Qu'est-ce que tu veux dire par « les bases » ? J'en sais beaucoup ! »

"Tellement naïf. Tu n'en connais pas la moitié. Dix pour cent au mieux."

« Tu plaisantes. Qu'est-ce qu'il y a d'autre ?

"Désérialisation d'une hiérarchie d'objets (désérialisation polymorphe), désérialisation de collections, et bien plus encore ! Le framework Jackson est vaste et puissant. Honnêtement, vous n'avez fait que commencer à effleurer la surface."

"D'accord, alors parle-moi de ça - je suis tout ouïe."

"J'apprécie vraiment de devenir plus intelligent à chaque leçon !"

"Eh bien, c'est avec plaisir que je t'aide, mon ami robot !"

"Êtes-vous prêt? Alors écoutez."

"Comme vous l'avez déjà appris, les annotations sont utilisées à la fois pour la sérialisation et la désérialisation. En pratique, la sérialisation nécessite beaucoup moins d'informations que la désérialisation. Par exemple :"

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

"Les instances de Array, ArrayList, LinkedList et d'autres classes sont transformées en tableaux JSON."

"Mais lorsque vous désérialisez un tableau JSON, quel objet devez-vous créer : une ArrayList ou une LinkedList ?"

"D'accord. Si un membre de classe est une interface (par exemple, public List<Cat> cats ), quel objet doit lui être attribué ?"

"Nous pouvons ajouter des annotations supplémentaires au champ ou indiquer explicitement les classes cibles lors de la désérialisation. Regardez cet exemple :"

Convertir un objet à partir de 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));
}
Une classe dont les objets sont désérialisés à partir de JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"En d'autres termes, nous pouvons utiliser le deuxième paramètre de la méthode mapper . readValue pour transmettre la liste des classes à utiliser lors de la désérialisation."

"J'aime ça. C'est pratique. Ainsi, vous pouvez désérialiser un tableau JSON en tout ce dont vous avez besoin, une ArrayList ou une LinkedList.

"Vous avez également mentionné l'utilisation d'annotations. Comment faites-vous cela ?"

"C'est facile. Par exemple :"

Convertir un objet à partir de 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);
}
Une classe dont les objets sont désérialisés à partir de JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Nous ajoutons simplement l'annotation @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) à la ligne 5 pour indiquer quelle implémentation de l'interface List utiliser."

"Ah. Je vois. C'est vraiment très simple."

"Mais il y a plus. Supposons que le type de données dans une liste soit aussi une interface ! Que feriez-vous ?"

"Est-ce que nous utilisons une annotation ici aussi ?"

"Oui, le même. Vous pouvez aussi l'utiliser pour indiquer le type de paramètre. Comme ceci :"

Type de collecte Comment définir le type de données
Liste @JsonDeserialize(contentAs = ValueTypeImpl.class)
Carte @JsonDeserialize(keyAs = KeyTypeImpl.class)

"Cool ! Il y a vraiment beaucoup d'annotations nécessaires pour diverses situations que nous ne pouvons pas anticiper."

"Ce n'est pas tout. Et cela nous amène au cours principal : dans les projets réels, les classes héritent assez souvent de la même classe ou interface de base, qui est utilisée pratiquement partout. Et maintenant, imaginez que vous deviez désérialiser une structure de données contenant de telles classes. Par exemple:"

Convertir un objet en 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());
}
Une classe dont les objets sont convertis en 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;
}
Résultat de sérialisation et sortie d'écran :
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

"Faites attention au résultat de la sérialisation."

"Nous ne pouvons pas désérialiser ces données dans un objet Java, car elles sont essentiellement indiscernables des données des autres classes."

"Il y a quelques caractéristiques distinctives : le chien a un champ propriétaire."

"Oui, mais ce champ pourrait être nul ou il pourrait être entièrement ignoré lors de la sérialisation."

"Eh bien, ne pouvons-nous pas spécifier le type de données à l'aide d'annotations que nous connaissons?"

"Non. Après la désérialisation, une seule collection devrait avoir divers objets Cat et Dog, ainsi qu'une douzaine d'autres classes qui pourraient hériter de Pet."

"Qu'est-ce que tu peux bien faire ici ?"

"Deux choses sont utilisées ici."

"Tout d'abord, un certain champ est choisi pour distinguer un type d'un autre. S'il n'y en a pas, alors il est créé."

"Deuxièmement, il y a des annotations spéciales qui vous permettent de contrôler le processus de "désérialisation polymorphe". Voici ce que vous pouvez faire :"

Convertir un objet en 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());
}
Une classe dont les objets sont convertis en 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<>();
}
Résultat de sérialisation et sortie d'écran :
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

À l'aide d'annotations, nous indiquons que la représentation JSON contiendra un champ spécial appelé type qui contiendra la valeur cat pour la classe Cat et la valeur dog pour la classe Dog . Cette information est suffisante pour bien désérialiser un objet : lors de la désérialisation, le type de l'objet à créer sera déterminé par la valeur du champ type.

"Parfois, le nom de la classe est utilisé comme valeur du champ de type (par exemple "com.example.entity.Cat.class"), mais ce n'est pas une bonne pratique. Comment une application externe recevant notre JSON connaîtrait-elle les noms de nos classes ? Pire, les classes sont parfois renommées. Il est préférable d'utiliser un nom unique pour identifier une classe spécifique.

"Cool ! Soupir. Je n'avais pas réalisé que la désérialisation était si compliquée. Et qu'il y a tellement de choses que vous pouvez affiner."

"Oui. Ce sont de nouveaux concepts pour vous, mais c'est le genre de connaissances pratiques qui feront de vous un programmeur de génie."

« Amigo est un programmeur cool. Cool !

"OK. Allez faire une pause."