# 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 - 再加上结构可以嵌套,进而可以用来表达复杂的数据 - 一个简单实例: ```json { "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 依赖 ```xml com.alibaba fastjson x.x.x ``` ### 2.2. Fastjson API #### 定义 Bean **Group.java** ```java public class Group { private Long id; private String name; private List users = new ArrayList(); 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 getUsers() { return users; } public void setUsers(List 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 依赖 ```xml com.fasterxml.jackson.core jackson-databind 2.9.8 ``` ### 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 persons = new LinkedList<>(); persons.add(p); persons.add(p2); persons.add(p3); Map 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 依赖 ```xml com.google.code.gson gson 2.8.6 ``` ### 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)