"¡Hola amigo!"

"Hola, Diego".

"Veo aquí que has aprendido los conceptos básicos de la serialización JSON".

"¿Qué quieres decir con 'básicos'? ¡Sé mucho!"

"Tan ingenuo. No sabes ni la mitad. Diez por ciento en el mejor de los casos".

"Estás bromeando. ¿Qué más hay?"

"¡Deserialización de una jerarquía de objetos (deserialización polimórfica), deserialización de colecciones y mucho más! El marco de Jackson es grande y poderoso. Honestamente, solo ha comenzado a arañar la superficie".

"Está bien, entonces cuéntamelo, soy todo oídos".

"¡Realmente disfruto volverme más inteligente con cada lección!"

"¡Bueno, es un placer ayudarte, mi amigo robot!"

"¿Estás listo? Entonces escucha".

"Como ya aprendió, las anotaciones se utilizan tanto para la serialización como para la deserialización. En la práctica, la serialización requiere mucha menos información que la deserialización. Por ejemplo:"

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

"Las instancias de Array, ArrayList, LinkedList y otras clases se convierten en matrices JSON".

"Pero cuando deserializa una matriz JSON, ¿qué objeto debe crear: una ArrayList o una LinkedList?"

"Correcto. Si un miembro de la clase es una interfaz (por ejemplo, List<Cat> cats pública ), ¿qué objeto se le debe atribuir?"

"Podemos agregar anotaciones adicionales al campo o indicar explícitamente las clases de destino durante la deserialización. Mire este ejemplo:"

Convertir un objeto 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));
}
Una clase cuyos objetos se deserializan de JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"En otras palabras, podemos usar el segundo parámetro del método mapper . readValue para pasar la lista de clases para usar durante la deserialización".

"Me gusta. Eso es conveniente. Así que puedes deserializar una matriz JSON en lo que necesites, una ArrayList o una LinkedList.

"También mencionaste usar anotaciones. ¿Cómo haces eso?"

"Es fácil. Por ejemplo:"

Convertir un objeto 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);
}
Una clase cuyos objetos se deserializan de JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Simplemente agregamos la anotación @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) a la línea 5 para indicar qué implementación de la interfaz List usar".

"Ah. Ya veo. Eso realmente es bastante simple".

"Pero hay más. ¡Supongamos que el tipo de datos en una Lista también es una interfaz! ¿Qué harías?"

"¿Usamos una anotación aquí también?"

"Sí, el mismo. También puedes usarlo para indicar el tipo de parámetro. Así:"

tipo de colección Cómo configurar el tipo de datos
Lista @JsonDeserialize(contentAs = ValueTypeImpl.class)
Mapa @JsonDeserialize(keyAs = KeyTypeImpl.class)

"¡Genial! Realmente se necesitan muchas anotaciones para varias situaciones que no podemos anticipar".

"Eso no es todo. Y eso nos lleva al tema principal: en proyectos reales, las clases a menudo heredan la misma clase base o interfaz, que se usa prácticamente en todas partes. Y ahora imagine que necesita deserializar una estructura de datos que contiene tales clases. Por ejemplo:"

Convertir un objeto a 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 clase cuyos objetos se convierten a 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;
}
Resultado de serialización y salida de pantalla:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

"Presta atención al resultado de la serialización".

"No podemos deserializar estos datos en un objeto Java, ya que es esencialmente indistinguible de los datos de otras clases".

"Hay algunas características distintivas: el perro tiene un campo de propietario".

"Sí, pero este campo podría ser nulo o podría omitirse por completo durante la serialización".

"Bueno, ¿no podemos especificar el tipo de datos usando anotaciones que conocemos?"

"No. Después de la deserialización, una sola colección debería tener varios objetos Cat y Dog, así como una docena de otras clases que podrían heredar de Pet".

"¿Qué diablos puedes hacer aquí?"

"Aquí se usan dos cosas".

"Primero, se elige un determinado campo para distinguir un tipo de otro. Si no hay uno, entonces se crea".

"En segundo lugar, hay anotaciones especiales que le permiten controlar el proceso de "deserialización polimórfica". Esto es lo que puede hacer:"

Convertir un objeto a 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 clase cuyos objetos se convierten a 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<>();
}
Resultado de serialización y salida de pantalla:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Usando anotaciones, indicamos que la representación JSON contendrá un campo especial llamado type que contendrá el valor cat para la clase Cat y el valor dog para la clase Dog . Esta información es suficiente para deserializar correctamente un objeto: durante la deserialización, el tipo de objeto que se creará estará determinado por el valor del campo de tipo.

"A veces, el nombre de la clase se usa como el valor del campo de tipo (por ejemplo, «com.example.entity.Cat.class»), pero esto no es una buena práctica. ¿Cómo podría una aplicación externa que recibe nuestro JSON saber los nombres de nuestras clases? Peor aún, las clases a veces se renombran. Es mejor usar un nombre único para identificar una clase específica".

"¡Genial! Suspiro. No sabía que la deserialización fuera tan complicada. Y que hay tantas cosas que puedes ajustar".

"Sí. Estos son conceptos nuevos para ti, pero este es el tipo de conocimiento práctico que te convertirá en un genio programador".

"Amigo es un programador genial. ¡Genial!"

"Está bien. Ve y tómate un descanso".