“嗨兄弟!”
“嘿,迭戈。”
“我看到这里你已经学习了 JSON 序列化的基础知识?”
“‘基础’是什么意思?我知道很多!”
“太天真了。你连一半都不知道。最多只有百分之十。”
“开什么玩笑,还有什么?”
“对象层次结构的反序列化(多态反序列化)、集合的反序列化,以及更多!Jackson 框架庞大而强大。老实说,您才刚刚开始触及皮毛。”
“好吧,那就告诉我吧——我洗耳恭听。”
“我真的很享受每节课变得更聪明!”
“嗯,很高兴能帮上忙,我的机器人朋友!”
“准备好了吗?那就听好了。”
“正如您已经了解到的,注释用于序列化和反序列化。实际上,序列化需要的信息远少于反序列化。例如:”
Java类 | JSON |
---|---|
|
|
|
|
|
|
“Array、ArrayList、LinkedList 和其他类的实例被转换为 JSON 数组。”
“但是当你反序列化一个 JSON 数组时,你应该创建什么对象:ArrayList 还是 LinkedList?”
“对。如果一个类成员是一个接口(例如public List<Cat> cats),应该将什么对象归于它?”
“我们可以在字段中添加额外的注释,或者在反序列化过程中明确指出目标类。看这个例子:”
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() {
}
}
“换句话说,我们可以使用映射器的第二个参数.readValue方法来传递要在反序列化期间使用的类列表。”
“我喜欢它。这很方便。因此您可以将 JSON 数组反序列化为您需要的任何内容,ArrayList 或 LinkedList。
“你还提到了使用注解。你是怎么做到的?”
“这很简单。例如:”
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() {
}
}
“我们只需将注释@JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)添加到第 5 行,以指示要使用 List 接口的哪个实现。”
“啊,原来如此。那还真是很简单呢。”
“但还有更多。假设List中的数据类型也是接口!你会怎么做?”
“我们在这里也使用注释吗?”
“是的,同一个。你也可以用它来表示参数类型。像这样:”
集合类型 | 如何设置数据类型 |
---|---|
列表 | @JsonDeserialize(contentAs = ValueTypeImpl.class) |
地图 | @JsonDeserialize(keyAs = KeyTypeImpl.class) |
“酷!对于我们无法预料的各种情况,确实需要很多注释。”
“这还不是全部。这让我们进入了主要课程:在实际项目中,类经常继承相同的基类或接口,几乎无处不在。现在想象一下,您需要反序列化包含此类的数据结构。例如:”
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"}
]
“注意连载结果。”
“我们无法将这些数据反序列化为 Java 对象,因为它基本上与其他类的数据没有区别。”
“有一些显着特征: Dog 有一个 owner 字段。”
“是的,但这个字段可以为空,也可以在序列化期间完全跳过。”
“嗯,我们不能使用我们知道的注释来指定数据类型吗?”
“不。反序列化后,单个集合应该有各种 Cat 和 Dog 对象,以及十几个可以从 Pet 继承的其他类。”
“你到底能在这里做什么?”
“这里用到了两样东西。”
“首先,选择某个字段来区分一种类型。如果没有,则创建它。”
“其次,有一些特殊的注释可以让你控制 «多态反序列化» 的过程。这是你可以做的:”
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}
]
}
使用注释,我们指出 JSON 表示将包含一个名为type 的特殊字段,它将保存Cat类的值cat和Dog类的值dog。此信息足以正确反序列化对象:在反序列化过程中,要创建的对象的类型将由类型字段的值确定。
“有时类名用作类型字段的值(例如 «com.example.entity.Cat.class»),但这不是一个好的做法。接收我们的 JSON 的外部应用程序如何知道名称我们的班级?更糟糕的是,班级有时会被重命名。最好使用一些唯一的名称来标识特定的班级。”
“太棒了!叹息。我没有意识到反序列化如此复杂。而且您可以微调的地方如此之多。”
“是的。这些对你来说是新概念,但这是一种能让你成为天才程序员的实用知识。”
“阿米戈是个很酷的程序员。酷!”
“行了,你去休息吧。”