“嗨兄弟!”

“嘿,迭戈。”

“我看到这里你已经学习了 JSON 序列化的基础知识?”

“‘基础’是什么意思?我知道很多!”

“太天真了。你连一半都不知道。最多只有百分之十。”

“开什么玩笑,还有什么?”

“对象层次结构的反序列化(多态反序列化)、集合的反序列化,以及更多!Jackson 框架庞大而强大。老实说,您才刚刚开始触及皮毛。”

“好吧,那就告诉我吧——我洗耳恭听。”

“我真的很享受每节课变得更聪明!”

“嗯,很高兴能帮上忙,我的机器人朋友!”

“准备好了吗?那就听好了。”

“正如您已经了解到的,注释用于序列化和反序列化。实际上,序列化需要的信息远少于反序列化。例如:”

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 数组时,你应该创建什么对象:ArrayList 还是 LinkedList?”

“对。如果一个类成员是一个接口(例如public List<Cat> cats),应该将什么对象归于它?”

“我们可以在字段中添加额外的注释,或者在反序列化过程中明确指出目标类。看这个例子:”

从 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&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

“换句话说,我们可以使用映射器的第二个参数.readValue方法来传递要在反序列期间使用的类列表。”

“我喜欢它。这很方便。因此您可以将 JSON 数组反序列化为您需要的任何内容,ArrayList 或 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&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

“我们只需将注释@JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)添加到第 5 行,以指示要使用 List 接口的哪个实现。”

“啊,原来如此。那还真是很简单呢。”

“但还有更多。假设List中的数据类型也是接口!你会怎么做?”

“我们在这里也使用注释吗?”

“是的,同一个。你也可以用它来表示参数类型。像这样:”

集合类型 如何设置数据类型
列表 @JsonDeserialize(contentAs = ValueTypeImpl.class)
地图 @JsonDeserialize(keyAs = KeyTypeImpl.class)

“酷!对于我们无法预料的各种情况,确实需要很多注释。”

“这还不是全部。这让我们进入了主要课程:在实际项目中,类经常继承相同的基类或接口,几乎无处不在。现在想象一下,您需要反序列化包含此类的数据结构。例如:”

将对象转换为 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 对象,因为它基本上与其他类的数据没有区别。”

“有一些显着特征: Dog 有一个 owner 字段。”

“是的,但这个字段可以为空,也可以在序列化期间完全跳过。”

“嗯,我们不能使用我们知道的注释来指定数据类型吗?”

“不。反序列化后,单个集合应该有各种 Cat 和 Dog 对象,以及十几个可以从 Pet 继承的其他类。”

“你到底能在这里做什么?”

“这里用到了两样东西。”

“首先,选择某个字段来区分一种类型。如果没有,则创建它。”

“其次,有一些特殊的注释可以让你控制 «多态反序列化» 的过程。这是你可以做的:”

将对象转换为 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&ltPet> 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 的外部应用程序如何知道名称我们的班级?更糟糕的是,班级有时会被重命名。最好使用一些唯一的名称来标识特定的班级。”

“太棒了!叹息。我没有意识到反序列化如此复杂。而且您可以微调的地方如此之多。”

“是的。这些对你来说是新概念,但这是一种能让你成为天才程序员的实用知识。”

“阿米戈是个很酷的程序员。酷!”

“行了,你去休息吧。”