"Здрасти приятел!"

— Хей, Диего.

„Виждам, че сте научor основите на JSON сериализацията?“

„Какво имаш предвид „основи“? Знам много!“

— Толкова наивен. Не знаеш и половината от това. В най-добрия случай десет процента.

"Шегуваш се. Какво друго има?"

„Десериализация на обектна йерархия (полиморфна десериализация), десериализация на колекции и много повече! Рамката на Jackson е голяма и мощна. Честно казано, вие едва започнахте да надраскате повърхността.“

„Добре, тогава ми разкажи за това – целият съм в ушите.“

„Наистина се наслаждавам да ставам по-умен с всеки урок!“

„Е, за мен е удоволствие да помогна, приятелю робот!“

"Готови ли сте? Тогава слушайте."

„Както вече научихте, анотациите се използват Howто за сериализация, така и за десериализация. На практика сериализацията изисква много по-малко информация от десериализацията. Например:“

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

„Екземпляри на Array, ArrayList, LinkedList и други класове се превръщат в JSON масиви.“

„Но когато десериализирате JSON масив, Howъв обект трябва да създадете: ArrayList or LinkedList?“

"Точно. Ако член на клас е интерфейс (напр. public List<Cat> cats ), Howъв обект трябва да му бъде приписан?"

„Можем да добавим допълнителни анотации към полето or изрично да посочим целевите класове по време на десериализация. Вижте този пример:“

Конвертиране на обект от 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));
}
Клас, чиито обекти са десериализирани от JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List<Cat> cats = new ArrayList<>();
 Cat() {
 }
}

"С други думи, можем да използваме втория параметър на метода mapper . readValue , за да предадем списъка с класове, които да се използват по време на десериализация."

„Харесва ми. Това е удобно. Така че можете да десериализирате JSON масив в Howвото ви трябва, ArrayList or LinkedList.

„Споменахте също използването на анотации. Как го правите?“

„Лесно е. Например:“

Конвертиране на обект от 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);
}
Клас, чиито обекти са десериализирани от JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List<Cat> cats = new ArrayList<>();
 Cat() {
 }
}

„Просто добавяме анотацията @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) към ред 5, за да посочим коя реализация на интерфейса List да се използва.“

"А, разбирам. Това наистина е много просто."

„Но има още нещо. Да предположим, че типът данни в списък също е интерфейс! Какво бихте направor?“

„И тук ли използваме анотация?“

„Да, същият. Можете също да го използвате, за да посочите типа на параметъра. Подобно на това:“

Тип колекция Как да зададете типа данни
списък @JsonDeserialize(contentAs = ValueTypeImpl.class)
Карта @JsonDeserialize(keyAs = KeyTypeImpl.class)

„Готино! Наистина са необходими много анотации за различни ситуации, които не можем да предвидим.“

„Това не е всичко. И това ни води до основния курс: в реални проекти класовете доста често наследяват един и същ базов клас or интерфейс, който се използва практически навсякъде. А сега си представете, че трябва да десериализирате структура от данни, съдържаща такива класове. Например:"

Преобразувайте обект в 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());
}
Клас, чиито обекти се конвертират в 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;
}
Резултат от сериализация и екранен изход:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

„Обърнете внимание на резултата от сериализацията.“

„Не можем да десериализираме тези данни в Java обект, тъй като по същество е неразличим от данните за други класове.“

„Има някои отличителни характеристики: Кучето има поле за собственик.“

„Да, но това поле може да е нула or може да бъде напълно пропуснато по време на сериализацията.“

„Е, не можем ли да посочим типа данни, като използваме анотации, които знаем?“

"Не. След десериализацията една колекция трябва да има различни обекти Cat и Dog, Howто и дузина други класове, които могат да наследят от Pet."

— Какво, за бога, можеш да правиш тук?

— Тук се използват две неща.

"Първо се избира определено поле, за да се разграничи един тип от друг. Ако няма такова, то се създава."

„Второ, има специални анотации, които ви позволяват да контролирате процеса на «полиморфна десериализация». Ето Howво можете да направите:

Преобразувайте обект в 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());
}
Клас, чиито обекти се конвертират в 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<Pet> pets = new ArrayList<>();
}
Резултат от сериализация и екранен изход:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Използвайки анотации, ние показваме, че JSON представянето ще съдържа специално поле, наречено type , което ще съдържа стойността cat за класа Cat и стойността dog за класа Dog . Тази информация е достатъчна за правилното десериализиране на обект: по време на десериализацията типът на обекта, който ще бъде създаден, ще се определя от стойността на полето тип.

„Понякога името на класа се използва като стойност на полето тип (напр. «com.example.entity.Cat.class»), но това не е добра практика. Как външно приложение, получаващо нашия JSON, ще знае имената на нашите класове? Още по-лошо, класовете понякога се преименуват. По-добре е да използвате няHowво уникално име, за да идентифицирате конкретен клас."

„Готино! Въздишка. Не знаех, че десериализацията е толкова сложна. И че има толкова много неща, които можете да настроите фино.“

"Да. Това са нови концепции за вас, но това е видът практически знания, които ще ви направят гениален програмист."

„Амиго е готин програмист. Готин!“

"ОК. Отиди и си почини."