Gson的简单使用

转:

       Gson——用java-JSON实现序列化和反序列化

       Gson——空值(Null)的映射

       Gson——嵌套对象的映射

       Gson——Map的映射



Gson 依赖

本指南将要着手,首先在一分钟内完成一些序列化的准备工作。
由于大多数读者都是Android开发者,我们会为你量身定制,但是Gson也能被用在任何Java环境中。在我们开始之前,我们需要将Gson库拖到我们的项目工程中。截止到写作时间,最新的版本是2.6.2。如果你正在使用Gradle,添加下面的代码:

compile 'com.google.code.gson:gson:2.6.2'

如果你正在使用Maven,你可以添加下面的依赖:

<dependencies>  
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.6.2</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

对于那些都没有使用依赖环境系统的可怜家伙,你可以从官方的Github仓库里下载jar包。

Java-JSON序列化基础

让我们做一些系列化!在Gson中序列化是指映射一个java对象到它的JSON表达。在后续的教程中,我们的数据将会变得更复杂,但是我们现在只要从一些非常简单的UserSimple开始:

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

user对象有四个属性:

  • user的 name 是一个 String 对象
  • user的 email 是一个 String 对象
  • user的 age 是一个 integer, 表明是按年来表示的(例如 26 ,并非准确的生日!)
  • 一个 boolean 标签 isDeveloper

我们的Android或java应用需要转换一个UserSimple对象到它的JSON表示。假设我们保持成员变量名字一致,我们会为Norman(这篇文章的作者)准备这样的JSON表示:

{
  "name": "Norman",
  "email": "norman@futurestud.io",
  "age": 26,
  "isDeveloper": true
}

让我们研究怎么用Gson完成转换。首先,我们需要为Norman创建一个Java对象。

UserSimple userObject = new UserSimple(  
    "Norman", 
    "norman@futurestud.io", 
    26, 
    true
);

译者注:如果没有在UserSimple类里添加对应的构造函数,上面会报错。添加也很简单,UserSimple类内部编辑界面,右键—>Generate->Constructor->全部选中 ,点击ok。自动生成如下代码:

public UserSimple(String name, String email, int age, boolean isDeveloper) {
    this.name = name;
    this.email = email;
    this.age = age;
    this.isDeveloper = isDeveloper;
}

为了完成序列化,我们需要一个Gson对象来操作转换。我们可以简单的使用下面的创建:

Gson gson = new Gson();

为了开始序列化,我们需要调用toJson()方法,然后传递UserSimple对象:

String userJson = gson.toJson(userObject);

userJson对象包含了下面的值:

{
  "age": 26,
  "email": "norman@futurestud.io",
  "isDeveloper": true,
  "name": "Norman"
}

Gson改变了属性的顺序(按字母顺序),但是内容是一样的!注意Gson是如何表示这些类型的。String值用“”包裹,但integer值却没有包裹。我们不需要在JSON对象或复制单个成员上浪费时间。一个Gson的简单调用足以映射整个对象。当我们处理非常复杂的数据结构时,这是非常方便的。但在我们进一步深入之前,我们测试另一个方向。Gson可以从上面的JSON数据中创建一个java对象么?

java-JSON 反序列化基础

首先,我们需要创建一个字符串,包含上面提到的JSON:

String userJson = "{'age':26,'email':'norman@futurestud.io','isDeveloper':true,'name':'Norman'}";

我们将 "变为 ' ,是为了避免大量的\"转义。它就是这样工作的。下一步,可能你已经猜到了,创建一个Gson实例:

Gson gson = new Gson();

最后,我们必须用fromJson()映射一个JSON到一个Java对象:

UserSimple userObject = gson.fromJson(userJson, UserSimple.class);

注意我们是怎样将Java对象作为第二个参数进行传递。否则Gson不知道应该将JSON映射。它不是一个魔术师!

如果我们添加了一个debugger并且检查了user对象的结果,它会展示Gson成功地准确映射了所有属性:





出现Null值,会发生什么?

我们假设你已经看了第一篇Gson的文章,它介绍了一个UserSimple类和它的成员变量。在之前的例子中,所有的值都是设定好的。如果他们是空值(null)呢?例如,我们创建了一个用户,他有邮箱、年龄并且是一个开发者,但是名字是null

UserSimple userObject = new UserSimple(null, "norman@futurestud.io", 26, true);

这是有效的Java代码,会创建一个普通的UserSimple对象。如果我们现在让Gson去创建匹配的JSON,将会是什么样子的呢?

UserSimple userObject = new UserSimple(null, "norman@futurestud.io", 26, true);

Gson gson = new Gson();  
String userJson = gson.toJson(userObject); // userJson = ??

Gson将会创建下面的JSON数据:

{
  "age": 26,
  "email": "norman@futurestud.io",
  "isDeveloper": true
}

在序列化中,Gson简单地忽略了null值。如果某个值没有设置,它根本不会成为解析出的JSON中的一部分。如果你需要那个null值的字段也在JSON中,Gson有这么一个选项可以设置。但要到后面才讲到。

现在明确下很重要的一点:在序列化过程中Gson不会为null值创建任何JSON数据。

反序列化过程也非常相似。假设我们有下面的JSON数据,但缺少了邮箱(email):

{
  "age": 26,
  "isDeveloper": true,
  "name": "Norman"
}

即使邮箱(email)缺失了,Gson仍然会尽力尝试映射。它会为那些不存在的字段设置为null:


当然,设置一个字段为null非常简单,但是如果一个字段并不是像String一样可以设置null呢?例如,想像一下下面的JSON:

{
  "email": "norman@futurestud.io",
  "name": "Norman"
}

ageint类型)和isDeveloper(boolean类型)并不接受null值。在这个例子中,Gson任然不会报错,会设置它们默认的值(分别设置0false):






嵌套对象的序列化

我们喜欢用实际的例子来演示功能,所以,让我们扩展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里,没有类或引用。只有嵌套nest)用户地址到用户对象中。基本地,在JSON里我们只要用{}在字段名字后创建一个新的对象:

{
    "age": 26,
    "email": "norman@futurestud.io",
    "isDeveloper": true,
    "name": "Norman",

    "userAddress": {
        "city": "Magdeburg",
        "country": "Germany",
        "houseNumber": "42A",
        "street": "Main Street"
    }
}

不同于其他属性(ageemail,...),新的userAddress没有直接的值。而是有一些包含在{}内的子值。很重要的一点,需要你理解字段名字后面有括号的就是表明这是一个嵌套对象。

太多的理论。是时候见识一下Gson从UserNested对象中创建了什么。你可能认识这个模式,Gson不需要任何配置。它会自动根据传进的类推断出数据结构:

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正确地创建了嵌套userAddressJSON对象。当然,我们可以为用户的薪水方法或工作地址增加更多的嵌套对象。甚至嵌套对象里也能有嵌套对象!

在下一节中,我们会研究另一个方向。如何反序列化复杂的嵌套JSON到Java对象呢?

嵌套对象的反序列化

上一节中,我们假设模型已经有了,并且我们只要创建一个匹配的JSON。特别是对于在真实世界里的开发者,经常是相反的。API接口正在返回一些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创建一个String字符串,以及另外三个扩展的Java类?有些人自已实现了代码,可能与下面的结果不同。事实上,创建一个Java对象并不需要太明确。例如,基于JSON,我们看到了有一样结构的cookwatier嵌套对象。你可以仍然创建一个不同的类,像上面做的一样,或者为他们俩只创建一个普通的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类到哪都能适用,我们作了一点弊,第一部分重复使用了UserAddress。 ;-)

总之,我们希望你理解从一个JSON字符串创建Java模型类的过程。你需要从高层到最深层次遍历,直到你的嵌套JSON只剩下常规类型。

既然主要工作已经完成了,我们可以把一切交给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里的所有的信息 :


提示:从JSON创建Java模型类是一项单调乏味的工作。在你掌握了概念后,你可能想要使用工具自动化处理。我们相当喜欢用jsonschema2pojo.org





Java中Map的序列化

Java的Map是一个非常灵活的数据类型,可以被用在各种各样的场景中。它允许我们开发者用Java语言去物化许多真实世界的情况。由于Java的Map使用非常广泛,我们可能不会覆盖到你正在用的案例,但将会覆盖到所有的方法。

让我们用一个场景来开始,你的应用有员工姓名的列表,你被要求去实现一个以某个特定字母开头的所有员工展示界面。例如,用户可以选择字母A,此时,你的应用会返回三个匹配的员工名字:AndreasAdenArnold。第一次迭代只是列出了所有名字的列表,但是性能往往是不够的。因此,转去使用HashMap实现,第一个字母作为键(Key)(如:A),值将是一个名字的列表。

如果我们创建我们的HashMap,我们的Java代码将会是这样:

HashMap<String, List<String>> employees = new HashMap<>();  
employees.put("A", Arrays.asList("Andreas", "Arnold", "Aden"));  
employees.put("C", Arrays.asList("Christian", "Carter"));  
employees.put("M", Arrays.asList("Marcus", "Mary"));

Map的序列化与其他类型是一样的,你可以将其直接传给Gson,Gson会正确地处理:

Gson gson = new Gson();  
String employeeJson = gson.toJson(employees);

返回的JSON数据:

{
  "M": [
    "Marcus",
    "Mary"
  ],
  "C": [
    "Christian",
    "Carter"
  ],
  "A": [
    "Andreas",
    "Arnold",
    "Aden"
  ]
}

每个键(A,CM)都有自己的名字列表,这正是我们想要的。

Java中Map的反序列化

如果你看了前面一节的JSON数据,或者下面的JSON数据。你会问你自己:如何才能区分一个集合和多个对象呢?答案很简单:你不能。这是为数不多的几个特性之一,JSON数据表示是模棱两可的。看看下面的列子:

{
  "1$": {
    "amount": 1,
    "currency": "Dollar"
  },
  "2$": {
    "amount": 2,
    "currency": "Dollar"
  },
  "3€": {
    "amount": 3,
    "currency": "Euro"
  }
}

在JSON中,读者可以假设有名字分别是1$2$3€的3个对象,每个对象都有自己的值。但另一方面也能被看作是一个简单的Map,1$2$3€是键(Key)。

没有可靠的方法评估一个JSON数据类型是什么。这里提供一些关键点来帮助你:

  • 第一且最重要的:上下文知识!如果你有文档或已经知道所描述的对象应该是怎样的,你应该能够区分单独的对象和Map数据。
  • 值的数据类型是一致的吗?一致的就可能是Map
  • 对象名称/键是动态的和广泛的吗?这也是Map类型的提示。

我们可以使用在列表对象文章中展示的TypeToken方法。你用我们期望的数据类型通过创建一个新的TypeToken得到一个Type方法:

public class AmountWithCurrency {  
    String currency;
    int amount;
}

String dollarJson = "{ '1$': { 'amount': 1, 'currency': 'Dollar'}, '2$': { 'amount': 2, 'currency': 'Dollar'}, '3€': { 'amount': 3, 'currency': 'Euro'} }";

Gson gson = new Gson();

Type amountCurrencyType =  
    new TypeToken<HashMap<String, AmountWithCurrency>>(){}.getType();

HashMap<String, AmountWithCurrency> amountCurrency =  
    gson.fromJson(dollarJson, amountCurrencyType);

amountCurrency变量实际上持有了整个正确的键、值集合:


嵌套的Map

Map数据结构不会成为模型/JSON的根元素。它只能是一个对象的属性。你得用像处理列表一样的方式序列化和反序列化它。我们也在这里发布了为嵌套行为的指导方式。



文/签到钱就到(简书作者)
原文链接:http://www.jianshu.com/p/c75d67371f84
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值