该文章翻译自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": "norman@futurestud.io",
"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",
"norman@futurestud.io",
26,
true,
userAddress
);
Gson gson = new Gson();
String userWithAddressJson = gson.toJson(userObject);
userWithAddressJson字符串的值是有趣的:
{
"age": 26,
"email": "norman@futurestud.io",
"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。