# Java 与 JSON

> JSON（JavaScript Object Notation）是一种基于文本的数据交换格式。几乎所有的编程语言都有很好的库或第三方工具来提供基于 JSON 的 API 支持，因此你可以非常方便地使用任何自己喜欢的编程语言来处理 JSON 数据。
>
> 本文主要从 Java 语言的角度来讲解 JSON 的应用。

* [1. JSON 简介](#1-json-简介)
  * [JSON 是什么](#json-是什么)
  * [优缺点、标准与 schema](#优缺点标准与-schema)
  * [相关技术以及与 XML 的关系](#相关技术以及与-xml-的关系)
  * [工具](#工具)
  * [1.2. Java JSON 库](#12-java-json-库)
* [JSON 编码指南](#json-编码指南)
  * [4.1 Google JSON 风格指南](#41-google-json-风格指南)
  * [4.2 使用 JSON 实现 API](#42-使用-json-实现-api)
* [2. Fastjson 应用](#2-fastjson-应用)
  * [2.1. 添加 maven 依赖](#21-添加-maven-依赖)
  * [2.2. Fastjson API](#22-fastjson-api)
  * [2.3. Fastjson 注解](#23-fastjson-注解)
* [3. Jackson 应用](#3-jackson-应用)
  * [3.1. 添加 maven 依赖](#31-添加-maven-依赖)
  * [3.2. Jackson API](#32-jackson-api)
  * [3.3. Jackson 注解](#33-jackson-注解)
* [4. Gson 应用](#4-gson-应用)
  * [4.1. 添加 maven 依赖](#41-添加-maven-依赖)
  * [4.2. Gson API](#42-gson-api)
  * [4.3. Gson 注解](#43-gson-注解)
* [5. 示例源码](#5-示例源码)
* [6. 参考资料](#6-参考资料)

## 1. JSON 简介

### JSON 是什么

JSON 起源于 1999 年的 [JS 语言规范 ECMA262 的一个子集](http://javascript.crockford.com/)（即 15.12 章节描述了格式与解析），后来 2003 年作为一个数据格式[ECMA404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)（很囧的序号有不有？）发布。 2006 年，作为 [rfc4627](http://www.ietf.org/rfc/rfc4627.txt) 发布，这时规范增加到 18 页，去掉没用的部分，十页不到。

JSON 的应用很广泛，这里有超过 100 种语言下的 JSON 库：[json.org](http://www.json.org/)。

更多的可以参考这里，[关于 json 的一切](https://github.com/burningtree/awesome-json)。

### 优缺点、标准与 schema

#### 结构与类型

这估计是最简单标准规范之一：

* 只有两种结构：对象内的键值对集合结构和数组，对象用 `{}` 表示、内部是”key”:”value”，数组用 `[]` 表示，不同值用逗号分开
* 基本数值有 7 个： false / null / true / object / array / number / string
* 再加上结构可以嵌套，进而可以用来表达复杂的数据
* 一个简单实例：

```javascript
{
    "Image": {
        "Width": 800,
        "Height": 600,
        "Title": "View from 15th Floor",
        "Thumbnail": {
            "Url": "http://www.example.com/image/481989943",
            "Height": 125,
            "Width": "100"
        },
        "IDs": [116, 943, 234, 38793]
    }
}
```

> 扩展阅读：
>
> * <http://www.json.org/json-zh.html> - 图文并茂介绍 json 数据形式
> * [json 的 RFC 文档](http://tools.ietf.org/html/rfc4627)

#### 2.2 优点

* 基于纯文本，所以对于人类阅读是很友好的。
* 规范简单，所以容易处理，开箱即用，特别是 JS 类的 ECMA 脚本里是内建支持的，可以直接作为对象使用。
* 平台无关性，因为类型和结构都是平台无关的，而且好处理，容易实现不同语言的处理类库，可以作为多个不同异构系统之间的数据传输格式协议，特别是在 HTTP/REST 下的数据格式。

#### 2.3 缺点

缺点也很明显：

* 性能一般，文本表示的数据一般来说比二进制大得多，在数据传输上和解析处理上都要更影响性能。
* 缺乏 schema，跟同是文本数据格式的 XML 比，在类型的严格性和丰富性上要差很多。XML 可以借由 XSD 或 DTD 来定义复杂的格式，并由此来验证 XML 文档是否符合格式要求，甚至进一步的，可以基于 XSD 来生成具体语言的操作代码，例如 apache xmlbeans。并且这些工具组合到一起，形成一套庞大的生态，例如基于 XML 可以实现 SOAP 和 WSDL，一系列的 ws-\*规范。但是我们也可以看到 JSON 在缺乏规范的情况下，实际上有更大一些的灵活性，特别是近年来 REST 的快速发展，已经有一些 schema 相关的发展(例如[理解 JSON Schema](https://spacetelescope.github.io/understanding-json-schema/index.html)，[使用 JSON Schema](http://usingjsonschema.com/downloads/)， [在线 schema 测试](http://azimi.me/json-schema-view/demo/demo.html))，也有类似于 WSDL 的[WADL](https://www.w3.org/Submission/wadl/)出现。

### 相关技术以及与 XML 的关系

* 使用 JSON 实现 RPC（类似 XML-RPC）：[JSON-RPC](http://www.jsonrpc.org/)
* 使用 JSON 实现 path 查询操作（类似 XML-PATH）：[JsonPATH](https://github.com/json-path/JsonPath)
* 在线查询工具：[JsonPATH](http://jsonpath.com/)

### 工具

* 格式化工具：[jsbeautifier](http://jsbeautifier.org/)
* chrome 插件：[5 个 Json View 插件](http://www.cnplugins.com/zhuanti/five-chrome-json-plugins.html)
* 在线 Mock: [在线 mock](https://www.easy-mock.com/)
* 其他 Mock：[SoapUI](https://www.soapui.org/rest-testing-mocking/rest-service-mocking.html)可以支持，SwaggerUI 也可以，[RestMock](https://github.com/andrzejchm/RESTMock)也可以。

### 1.2. Java JSON 库

Java 中比较流行的 JSON 库有：

* [Fastjson](https://github.com/alibaba/fastjson) - 阿里巴巴开发的 JSON 库，性能十分优秀。
* [Jackson](http://wiki.fasterxml.com/JacksonHome) - 社区十分活跃且更新速度很快。Spring 框架默认 JSON 库。
* [Gson](https://github.com/google/gson) - 谷歌开发的 JSON 库，目前功能最全的 JSON 库 。

从性能上来看，一般情况下：Fastjson > Jackson > Gson

## JSON 编码指南

### 4.1 Google JSON 风格指南

遵循好的设计与编码风格，能提前解决 80%的问题:

* 英文版[Google JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml)：<https://google.github.io/styleguide/jsoncstyleguide.xml>
* 中文版[Google JSON 风格指南](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md)：<https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md>

简单摘录如下：

* 属性名和值都是用双引号，不要把注释写到对象里面，对象数据要简洁
* 不要随意结构化分组对象，推荐是用扁平化方式，层次不要太复杂
* 命名方式要有意义，比如单复数表示
* 驼峰式命名，遵循 Bean 规范
* 使用版本来控制变更冲突
* 对于一些关键字，不要拿来做 key
* 如果一个属性是可选的或者包含空值或 null 值，考虑从 JSON 中去掉该属性，除非它的存在有很强的语义原因
* 序列化枚举类型时，使用 name 而不是 value
* 日期要用标准格式处理
* 设计好通用的分页参数
* 设计好异常处理

### 4.2 使用 JSON 实现 API

[JSON API](http://jsonapi.org.cn/format/)与 Google JSON 风格指南有很多可以相互参照之处。

[JSON API](http://jsonapi.org.cn/format/)是数据交互规范，用以定义客户端如何获取与修改资源，以及服务器如何响应对应请求。

JSON API 设计用来最小化请求的数量，以及客户端与服务器间传输的数据量。在高效实现的同时，无需牺牲可读性、灵活性和可发现性。

## 2. Fastjson 应用

> 扩展阅读：更多 API 使用细节可以参考：[JSONField 用法](https://github.com/alibaba/fastjson/wiki/JSONField)

### 2.1. 添加 maven 依赖

```markup
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>
</dependency>
```

### 2.2. Fastjson API

#### 定义 Bean

**Group.java**

```java
public class Group {

    private Long       id;
    private String     name;
    private List<User> users = new ArrayList<User>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public void addUser(User user) {
        users.add(user);
    }
}
```

**User.java**

```java
public class User {

    private Long   id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
```

**初始化 Bean**

```java
Group group = new Group();
group.setId(0L);
group.setName("admin");

User guestUser = new User();
guestUser.setId(2L);
guestUser.setName("guest");

User rootUser = new User();
rootUser.setId(3L);
rootUser.setName("root");

group.addUser(guestUser);
group.addUser(rootUser);
```

#### 序列化

```java
String jsonString = JSON.toJSONString(group);
System.out.println(jsonString);
```

#### 反序列化

```java
Group bean = JSON.parseObject(jsonString, Group.class);
```

### 2.3. Fastjson 注解

#### `@JSONField`

> 扩展阅读：更多 API 使用细节可以参考：[JSONField 用法](https://github.com/alibaba/fastjson/wiki/JSONField)，这里介绍基本用法。

可以配置在属性（setter、getter）和字段（必须是 public field）上。

```java
@JSONField(name="ID")
public int getId() {return id;}

// 配置date序列化和反序列使用yyyyMMdd日期格式
@JSONField(format="yyyyMMdd")
public Date date1;

// 不序列化
@JSONField(serialize=false)
public Date date2;

// 不反序列化
@JSONField(deserialize=false)
public Date date3;

// 按ordinal排序
@JSONField(ordinal = 2)
private int f1;

@JSONField(ordinal = 1)
private int f2;
```

#### `@JSONType`

* 自定义序列化：[ObjectSerializer](https://github.com/alibaba/fastjson/wiki/JSONType_serializer)
* 子类型处理：[SeeAlso](https://github.com/alibaba/fastjson/wiki/JSONType_seeAlso_cn)

JSONType.alphabetic 属性: fastjson 缺省时会使用字母序序列化，如果你是希望按照 java fields/getters 的自然顺序序列化，可以配置 JSONType.alphabetic，使用方法如下：

```java
@JSONType(alphabetic = false)
public static class B {
    public int f2;
    public int f1;
    public int f0;
}
```

## 3. Jackson 应用

> 扩展阅读：更多 API 使用细节可以参考 [jackson-databind 官方说明](https://github.com/FasterXML/jackson-databind)

### 3.1. 添加 maven 依赖

```markup
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>
```

### 3.2. Jackson API

#### 序列化

```java
ObjectMapper mapper = new ObjectMapper();

mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);
```

#### 反序列化

```java
ObjectMapper mapper = new ObjectMapper();

MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
```

#### 容器的序列化和反序列化

```java
Person p = new Person("Tom", 20);
Person p2 = new Person("Jack", 22);
Person p3 = new Person("Mary", 18);

List<Person> persons = new LinkedList<>();
persons.add(p);
persons.add(p2);
persons.add(p3);

Map<String, List> map = new HashMap<>();
map.put("persons", persons);

String json = null;
try {
    json = mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
    e.printStackTrace();
}
```

### 3.3. Jackson 注解

> 扩展阅读：更多注解使用细节可以参考 [jackson-annotations 官方说明](https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations)

#### `@JsonProperty`

```java
public class MyBean {
   private String _name;

   // without annotation, we'd get "theName", but we want "name":
   @JsonProperty("name")
   public String getTheName() { return _name; }

   // note: it is enough to add annotation on just getter OR setter;
   // so we can omit it here
   public void setTheName(String n) { _name = n; }
}
```

#### `@JsonIgnoreProperties` 和 `@JsonIgnore`

```java
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
// regardless of whether POJO has such properties
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean {
   // will not be written as JSON; nor assigned from JSON:
   @JsonIgnore
   public String internal;

   // no annotation, public field is read/written normally
   public String external;

   @JsonIgnore
   public void setCode(int c) { _code = c; }

   // note: will also be ignored because setter has annotation!
   public int getCode() { return _code; }
}
```

#### `@JsonCreator`

```java
public class CtorBean {
  public final String name;
  public final int age;

  @JsonCreator // constructor can be public, private, whatever
  private CtorBean(@JsonProperty("name") String name,
    @JsonProperty("age") int age)
  {
      this.name = name;
      this.age = age;
  }
}
```

#### `@JsonPropertyOrder`

alphabetic 设为 true 表示，json 字段按自然顺序排列，默认为 false。

```java
@JsonPropertyOrder(alphabetic = true)
public class JacksonAnnotationBean {}
```

## 4. Gson 应用

> 详细内容可以参考官方文档：[Gson 用户指南](https://github.com/google/gson/blob/master/UserGuide.md)

### 4.1. 添加 maven 依赖

```markup
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>
```

### 4.2. Gson API

#### 序列化

```java
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(10L); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]
```

#### 反序列化

```java
int i1 = gson.fromJson("1", int.class);
Integer i2 = gson.fromJson("1", Integer.class);
Long l1 = gson.fromJson("1", Long.class);
Boolean b1 = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
```

#### GsonBuilder

`Gson` 实例可以通过 `GsonBuilder` 来定制实例化，以控制其序列化、反序列化行为。

```java
Gson gson = new GsonBuilder()
        .setPrettyPrinting()
        .setDateFormat("yyyy-MM-dd HH:mm:ss")
        .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
        .create();
```

### 4.3. Gson 注解

#### `@Since`

[`@Since`](https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/annotations/Since.java) 用于控制对象的序列化版本。示例：

```java
public class VersionedClass {
  @Since(1.1) private final String newerField;
  @Since(1.0) private final String newField;
  private final String field;

  public VersionedClass() {
    this.newerField = "newer";
    this.newField = "new";
    this.field = "old";
  }
}

VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
System.out.println();

gson = new Gson();
jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
```

#### `@SerializedName`

`@SerializedName` 用于将类成员按照指定名称序列化、反序列化。示例：

```java
private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}
```

## 5. 示例源码

> 示例源码：[javalib-io-json](https://github.com/dunwu/java-tutorial/tree/master/javalib-io-json)

## 6. 参考资料

* **官方**
  * [Fastjson Github](https://github.com/alibaba/fastjson)
  * [Gson Github](https://github.com/google/gson)
  * [jackson 官方文档](https://github.com/FasterXML/jackson-docs)
  * [jackson-databind](https://github.com/FasterXML/jackson-databind)
* **文章**
  * <http://www.json.org/json-zh.html>
  * [json 的 RFC 文档](http://tools.ietf.org/html/rfc4627)
  * [JSON 最佳实践](https://kimmking.github.io/2017/06/06/json-best-practice/)
  * [【简明教程】JSON](https://www.jianshu.com/p/8b428e1d1564)
