Gson教程二(譯):巢狀物件的序列化和反序列化
該文章翻譯自Gson Tutorial Series系列教程。該篇主要闡述瞭如何使用Gson對映巢狀類。
巢狀物件的序列化
我們希望通過實際的例子來闡述功能,因此讓我們來擴充套件我們的UserSimple模型。在之前的釋出中,user模型僅僅有一些基本的Java型別:
public class UserSimple {
String name;
String email;
boolean isDeveloper;
int age;
}
現在,我們的user還擁有家庭住址,家庭住址有它自己的型別為UserAddress:
public class UserNested { String name; String email; boolean isDeveloper; int age; // new, see below! UserAddress userAddress; } public class UserAddress { String street; String houseNumber; String city; String country; }
換句話說,user現在由UserNested模型來表示了,在這個模型中添加了一個一對一關係的住址物件。住址由UserAddress模型表示。
在Java中,這兩個模型可以以類明確分離開來,然後通過建立UserAddress userAddress成員變數以保持一個引用。但是在Json中我們沒有類也沒有引用。唯一的方式(除了通過IDs然後將資料合併在一起的方式)就只能是將使用者住址嵌入到user物件中了,在JSON中我們在域名後建立了一個用{}包圍的新物件:
{ "age": 26, "email": "[email protected]", "isDeveloper": true, "name": "Norman", "userAddress": { "city": "Magdeburg", "country": "Germany", "houseNumber": "42A", "street": "Main Street" } }
不像其他的屬性(age,email,…)該新userAddress屬性沒有一個直接的值。相反,它包含一些子值並用{}包裹。理解域名後出現的大括號是非常重要的,這通常表示這是一個巢狀物件。
理論已經足夠。是時候瞭解Gson通過一個UserNested物件建立了什麼?你將可能認識該模型。Gson不需要任何的設定。它將會通過傳入的class型別自動推斷相應的資料結構。
UserAddress userAddress = new UserAddress(
"Main Street",
"42A",
"Magdeburg",
"Germany"
);
UserNested userObject = new UserNested(
"Norman",
" [email protected]",
26,
true,
userAddress
);
Gson gson = new Gson();
String userWithAddressJson = gson.toJson(userObject);
userWithAddressJson字串的值是有趣的:
{
"age": 26,
"email": "[email protected]",
"isDeveloper": true,
"name": "Norman",
"userAddress": {
"city": "Magdeburg",
"country": "Germany",
"houseNumber": "42A",
"street": "Main Street"
}
}
是的,Gson對域按字母重排序了,但結果無疑是我們希望的。Gson正確的建立了包裹著userAddress的JSON物件。當然,我們也可以新增更多的被包裹物件,比如使用者的付款方式或者工作地址。同樣,被包裹的物件也可以包裹其它物件。
在下一部分,我們轉向反面。我們怎樣才能反序列化一個複雜的,巢狀的JSON為Java物件。
巢狀物件的反序列化
在前一部分,我們假設模型就在那裡,我們需要做的僅僅是建立一個匹配的JSON。特別是對於真實世界中的應用開發者,通常還有另一種情況。API返回了某個JSON,然後我們需要根據這個JSON構造我們的型別。
如果你看過本部落格之前的章節,那麼你一定對如何建立模型類有所瞭解了。為了不使你感到囉嗦,我們將不適用user的例子了,而適用一個漂亮的小旅館。
{
"name": "Future Studio Steak House",
"owner": {
"name": "Christian",
"address": {
"city": "Magdeburg",
"country": "Germany",
"houseNumber": "42A",
"street": "Main Street"
}
},
"cook": {
"age": 18,
"name": "Marcus",
"salary": 1500
},
"waiter": {
"age": 18,
"name": "Norman",
"salary": 1000
}
}
這是呼叫我們的API所得到的結果,我們希望通過使用Gson自動建立匹配的Java物件。首先,你需要模型化一個基本的類,這個基本類包含了所有頂層的域:
public class Restaurant {
String name;
Owner owner;
Cook cook;
Waiter waiter;
}
看一下,我們是如何為name定義為字元型,而其他三個是如何定義類的?可能你會得出不同的結果。建立Java物件並不總是明確的。例如,基於該JSON,我們可以發現,cook和waiter的巢狀物件擁有相同的結構。你可以為這它們定義不同的類,就行上面那樣,或者你也可以它們定義一個共同的類 —— Staff:
public class Restaurant {
String name;
Owner owner;
Staff cook;
Staff waiter;
}
無論哪一種方式都是有效的。如果你不相信,我們通常傾向建立一個額外的類來避免將來的出現問題。比如,如果cook模型改變了但是waiter模型沒有改變,那麼你可能需要改變大量的程式碼。因此,現在我們拋棄Staff的解決方法。當然,我們依然需要為第二層的物件建立Java模型類:
public class Owner {
String name;
UserAddress address;
}
public class Cook {
String name;
int age;
int salary;
}
public class Waiter {
String name;
int age;
int salary;
}
好了,我們偷了一點懶,複用了開始的UserAddress。但是這僅僅是因為它能夠完美的匹配。
儘管如此,我們希望你能夠理解根據JSON字串建立Java模型類的過程。你需要從最高層一直深入到最底層,直到你的巢狀JSON只剩下常規的型別。
我們已經做了主要的工作,可以放心的將接下來的事情交給Gson了。當然,只有我們正確的做了我們該做的,Gson才會只需要很少的程式碼就能優雅的建立Java物件。
String restaurantJson = "{ 'name':'Future Studio Steak House', 'owner':{ 'name':'Christian', 'address':{ 'city':'Magdeburg', 'country':'Germany', 'houseNumber':'42', 'street':'Main Street'}},'cook':{ 'age':18, 'name': 'Marcus', 'salary': 1500 }, 'waiter':{ 'age':18, 'name': 'Norman', 'salary': 1000}}";
Gson gson = new Gson();
Restaurant restaurantObject = gson.fromJson(restaurantJson, Restaurant.class);
該restaurantObject包含了JSON中的所有資訊:
提示:根據JSONs建立Java模型類是一件非常繁瑣的工作。如果你已經意識到這一點那麼你肯定希望有工具可以自動的完成這一流程。這裡我們推薦jsonschema2pojo.org。