“嗨兄弟!”
“嘿,迭戈。”
“我看到這裡你已經學習了 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 的外部應用程序如何知道名稱我們的班級?更糟糕的是,班級有時會被重命名。最好使用一些唯一的名稱來標識特定的班級。”
“太棒了!嘆息。我沒有意識到反序列化如此復雜。而且您可以微調的地方如此之多。”
“是的。這些對你來說是新概念,但這是一種能讓你成為天才程序員的實用知識。”
“阿米戈是個很酷的程序員。酷!”
“行了,你去休息吧。”
GO TO FULL VERSION