"Oi Cara!"

"Oi, Diogo."

"Vejo aqui que você aprendeu o básico da serialização JSON?"

"O que você quer dizer com 'básico'? Eu sei muito!"

"Tão ingênuo. Você não sabe nem a metade. Dez por cento no máximo."

"Você está brincando. O que mais há?"

"Desserialização de uma hierarquia de objetos (desserialização polimórfica), desserialização de coleções e muito mais! A estrutura de Jackson é grande e poderosa. Honestamente, você apenas começou a arranhar a superfície."

"Ok, então me conte sobre isso – eu sou todo ouvidos."

"Estou gostando muito de ficar mais inteligente a cada aula!"

"Bem, é um prazer ajudar, meu amigo robô!"

"Você está pronto? Então escute."

"Como você já aprendeu, as anotações são usadas tanto para serialização quanto para desserialização. Na prática, a serialização requer muito menos informações do que a desserialização. Por exemplo:"

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

"Instâncias de Array, ArrayList, LinkedList e outras classes são transformadas em arrays JSON."

"Mas quando você desserializa um array JSON, que objeto você deve criar: um ArrayList ou um LinkedList?"

"Certo. Se um membro de classe é uma interface (por exemplo, public List<Cat> cats ), qual objeto deve ser atribuído a ele?"

"Podemos adicionar anotações adicionais ao campo ou indicar explicitamente as classes de destino durante a desserialização. Veja este exemplo:"

Converter um 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));
}
Uma classe cujos objetos são desserializados de JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Em outras palavras, podemos usar o segundo parâmetro do método mapper . readValue para passar a lista de classes a serem usadas durante a desserialização."

"Eu gosto disso. Isso é conveniente. Então você pode desserializar um array JSON em qualquer coisa que você precisa, um ArrayList ou um LinkedList.

"Você também mencionou o uso de anotações. Como você faz isso?"

"É fácil. Por exemplo:"

Converter um 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);
}
Uma classe cujos objetos são desserializados de JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Simplesmente adicionamos a anotação @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) à linha 5 para indicar qual implementação da interface List usar."

"Ah. Entendo. Isso realmente é bem simples."

"Mas há mais. Suponha que o tipo de dados em uma lista também seja uma interface! O que você faria?"

"Nós usamos uma anotação aqui também?"

"Sim, o mesmo. Você também pode usá-lo para indicar o tipo de parâmetro. Assim:"

Tipo de coleção Como definir o tipo de dados
Lista @JsonDeserialize(contentAs = ValueTypeImpl.class)
Mapa @JsonDeserialize(keyAs = KeyTypeImpl.class)

"Legal! Realmente são necessárias muitas anotações para várias situações que não podemos prever."

"Isso não é tudo. E isso nos leva ao curso principal: em projetos reais, as classes geralmente herdam a mesma classe ou interface base, que é usada praticamente em todos os lugares. E agora imagine que você precisa desserializar uma estrutura de dados contendo essas classes. Por exemplo:"

Converter um objeto em 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());
}
Uma classe cujos objetos são convertidos em 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 da serialização e saída da tela:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

"Preste atenção no resultado da serialização."

"Não podemos desserializar esses dados em um objeto Java, pois eles são essencialmente indistinguíveis dos dados de outras classes."

"Existem algumas características distintivas: Dog tem um campo de proprietário."

"Sim, mas este campo pode ser nulo ou pode ser totalmente ignorado durante a serialização."

"Bem, não podemos especificar o tipo de dados usando anotações que conhecemos?"

"Não. Após a desserialização, uma única coleção deve ter vários objetos Cat e Dog, bem como uma dúzia de outras classes que podem herdar de Pet."

"O que no mundo você pode fazer aqui?"

"Duas coisas são usadas aqui."

"Primeiro, um determinado campo é escolhido para distinguir um tipo do outro. Se não houver, então é criado."

"Em segundo lugar, existem anotações especiais que permitem controlar o processo de «desserialização polimórfica». Aqui está o que você pode fazer:"

Converter um objeto em 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());
}
Uma classe cujos objetos são convertidos em 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 da serialização e saída da tela:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Usando anotações, indicamos que a representação JSON conterá um campo especial chamado type que conterá o valor cat para a classe Cat e o valor dog para a classe Dog . Esta informação é suficiente para desserializar corretamente um objeto: durante a desserialização, o tipo do objeto a ser criado será determinado pelo valor do campo type.

"Às vezes, o nome da classe é usado como o valor do campo de tipo (por exemplo, «com.example.entity.Cat.class»), mas isso não é uma boa prática. Como um aplicativo externo recebendo nosso JSON saberia os nomes de nossas classes? Pior ainda, as classes às vezes são renomeadas. É melhor usar algum nome exclusivo para identificar uma classe específica."

"Legal! Suspiro. Não sabia que a desserialização era tão complicada. E que há tanto que você pode ajustar."

"Sim. Estes são novos conceitos para você, mas este é o tipo de conhecimento prático que fará de você um programador genial."

"Amigo é um programador legal. Legal!"

"OK. Vá e faça uma pausa."