"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 |
---|---|
|
|
|
|
|
|
"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 :"
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));
}
@JsonAutoDetect
class Cat {
public String name;
public List<Cat> 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 :"
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);
}
@JsonAutoDetect
class Cat
{
public String name;
@JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
public List<Cat> 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:"
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());
}
@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;
}
[
{ "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 :"
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());
}
@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<Pet> pets = new ArrayList<>();
}
{
"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."
GO TO FULL VERSION