"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 |
---|---|
|
|
|
|
|
|
"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:"
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() {
}
}
"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:"
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() {
}
}
"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:"
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"}
]
"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:"
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}
]
}
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."
GO TO FULL VERSION