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": "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中的所有信息:

Restaurant Object

提示:根据JSONs创建Java模型类是一件非常繁琐的工作。如果你已经意识到这一点那么你肯定希望有工具可以自动的完成这一流程。这里我们推荐jsonschema2pojo.org

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容