Gson 如何对嵌套对象进行映射

在 Gson — Java-JSON 序列化和反序列化入门 这篇文章中,我们介绍了Gson的基本功能。 在这篇文章中,我们将更多地使用更真实的数据并查看嵌套对象。 我们将了解使用包含额外复杂数据的对象是很容易的。

嵌套对象序列化

我们喜欢通过示例演示功能,所以让我们扩展我们的 UserSimple 模型。 在之前的文章中,用户模型只有几个标准的 Java 类型:

public class UserSimple {  
    String name;
    String email;
    boolean isDeveloper;
    int age;
}

现在我们的用户也有了一个家庭地址,它有自己的模型类 UserAddress

public class UserNested {  
    String name;
    String email;
    boolean isDeveloper;
    int age;

    // 新字段
    UserAddress userAddress;
}

public class UserAddress {  
    String street;
    String houseNumber;
    String city;
    String country;
}

换句话说,当前在 UserNested 模型中表示的用户与地址对象具有额外的一对一关系。 地址在 UserAddress 模型中表示。

在 Java 中,这两个模型可以通过类清晰地分开,我们通过 UserAddress userAddress 字段保留引用。 但是,在 JSON 中,我们没有类或引用。 JSON 中的唯一方法(以后不使用 ID 并将事物绑定在一起)是将用户地址嵌套在用户对象中。 基本上,在 JSON 中,我们只需在字段名称后使用 {} 创建一个新对象:

{
    "age": 26,
    "email": "jiyik_onmpw@163.com",
    "isDeveloper": true,
    "name": "jiyik",

    "userAddress": {
        "city": "beijing",
        "country": "china",
        "houseNumber": "42A",
        "street": "Main Street"
    }
}

与其他属性(age、email、…)不同,新的 userAddress 没有直接值。 相反,它有包含在 {} 中的子值。 立即将字段名称后面的括号理解为信号 this-is-a-nested-object 很重要。

理论够了。 是时候看看 Gson 从 UserNested 对象创建了什么。 我们可能会清晰的认出这种模式。 Gson 不需要任何配置。 它会根据传递的类自动推断数据的结构:

UserAddress userAddress = new UserAddress(
    "Main Street",
    "42A",
    "BeiJing",
    "China"
);

UserNested userObject = new UserNested(
    "jiyik",
    "jiyik_onmpw@163.com",
    true,
    26,
    userAddress
);
Gson gson = new Gson();
String userWithAddressJson = gson.toJson(userObject);

userWithAddressJson 字符串的值很有趣:

{
  "name": "jiyik",
  "email": "jiyik_onmpw@163.com",
  "isDeveloper": true,
  "age": 26,
  "userAddress": {
    "street": "Main Street",
    "houseNumber": "42A",
    "city": "BeiJing",
    "country": "China"
  }
}

结果完全符合我们的预期。 Gson 正确创建了嵌套的 userAddress JSON 对象。 当然,我们可以为用户的付款方式或工作地址添加更多的嵌套对象。 即使是嵌套对象也可以有嵌套对象!

在下一部分中,我们将看看另一个方向。 我们如何将复杂的嵌套 JSON 反序列化为 Java 对象?


嵌套对象反序列化

在上面,我们假设模型已经存在,我们只想创建一个匹配的 JSON。 尤其是对于现实世界中的应用程序开发人员来说,情况往往恰恰相反。 API 正在返回一些 JSON,我们需要为该 JSON 创建模型类。

如果大家已经阅读了这篇文章的前几段,那么大家已经对如何创建模型类有所了解。 下面的示例中我们转换一下场景,从用户示例转到一家不错的小餐厅。

{
  "name": "迹忆客美食馆",
  "owner": {
    "name": "jiyik",
    "address": {
      "city": "BeiJing",
      "country": "China",
      "houseNumber": "42A",
      "street": "Main Street"
    }
  },
  "cook": {
    "age": 18,
    "name": "Tom",
    "salary": 1500
  },
  "waiter": {
    "age": 18,
    "name": "Jerry",
    "salary": 1000
  }
}

这来自我们的 API,我们希望利用 Gson 自动为其创建匹配的 Java 对象。 首先,我们需要对所有一级字段所在的基类进行建模:

public class Restaurant {  
    String name;

    Owner owner;
    Cook cook;
    Waiter waiter;
}

看看我们如何为 name 创建一个 String 并为其他三个创建额外的 Java 类? 每个人可能得出了不同的结果。 创建 Java 对象并不总是明确的。 例如,基于 JSON,我们看到 cook 和 waiter 嵌套对象具有相同的结构。 我们仍然可以创建一个不同的类,就像我们在上面所做的那样,或者为两者创建一个普通的 Staff 类:

public class Restaurant {  
    String name;

    Owner owner;
    Staff cook;
    Staff 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。 当然,如果我们正确地完成了我们的工作,它将优雅地处理所有事情并只用几行代码创建 Java 对象:

String restaurantJson = "{ 'name':'迹忆客美食馆', 'owner':{ 'name':'jiyik', 'address':{ 'city':'BeiJing', 'country':'China', 'houseNumber':'42A', 'street':'Main Street'}},'cook':{ 'age':18, 'name': 'Tom', 'salary': 1500 }, 'waiter':{ 'age':18, 'name': 'Jerry', 'salary': 1000}}";

Gson gson = new Gson();

Restaurant restaurantObject = gson.fromJson(restaurantJson, Restaurant.class);  

restaurantObject 实际上包含Json中的所有信息:

Gson 如何对嵌套对象进行映射

提示:从 JSON 创建 Java 模型类可能是一项乏味的工作。 在掌握了这些概念之后,大家可能想要使用自动化该过程的工具。 我们推荐 jsonschema2pojo.org 。