文章目录
- Java 和 JSON 序列化
- JSON 简介
- JSON 是什么
- JSON 标准
- JSON 优缺点
- JSON 工具
- Java JSON 库
- JSON 编码指南
- Fastjson 应用
- 添加 maven 依赖
- Fastjson API
- 定义 Bean
- 序列化
- 反序列化
- Fastjson 注解
- `@JSONField`
- `@JSONType`
- Jackson 应用
- 添加 maven 依赖
- Jackson API
- 序列化
- 反序列化
- 容器的序列化和反序列化
- Jackson 注解
- `@JsonProperty`
- `@JsonIgnoreProperties` 和 `@JsonIgnore`
- `@JsonCreator`
- `@JsonPropertyOrder`
- Gson 应用
- 添加 maven 依赖
- Gson API
- 序列化
- 反序列化
- GsonBuilder
- Gson 注解
- `@Since`
- `@SerializedName`
- 参考资料
Java 和 JSON 序列化
JSON(JavaScript Object Notation)是一种基于文本的数据交换格式。几乎所有的编程语言都有很好的库或第三方工具来提供基于 JSON 的 API 支持,因此你可以非常方便地使用任何自己喜欢的编程语言来处理 JSON 数据。
JSON 简介
JSON 是什么
JSON 起源于 1999 年的 JS 语言规范 ECMA262 的一个子集,后来 2003 年作为一个数据格式ECMA404(404???)发布。
2006 年,作为 rfc4627 发布,这时规范增加到 18 页,去掉没用的部分,十页不到。
JSON 的应用很广泛,这里有超过 100 种语言下的 JSON 库:json.org。
更多的可以参考这里,关于 json 的一切。
JSON 标准
这是最简单标准规范之一:
- 只有两种结构:对象内的键值对集合结构和数组,对象用
{}
表示、内部是"key":"value"
,数组用[]
表示,不同值用逗号分开 - 基本数值有 7 个:
false
/null
/true
/object
/array
/number
/string
- 再加上结构可以嵌套,进而可以用来表达复杂的数据
- 一个简单实例:
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] }}
JSON 优缺点
优点:
- 基于纯文本,所以对于人类阅读是很友好的。
- 规范简单,所以容易处理,开箱即用,特别是 JS 类的 ECMA 脚本里是内建支持的,可以直接作为对象使用。
- 平台无关性,因为类型和结构都是平台无关的,而且好处理,容易实现不同语言的处理类库,可以作为多个不同异构系统之间的数据传输格式协议,特别是在 HTTP/REST 下的数据格式。
缺点:
- 性能一般,文本表示的数据一般来说比二进制大得多,在数据传输上和解析处理上都要更影响性能。
- 缺乏 schema,跟同是文本数据格式的 XML 比,在类型的严格性和丰富性上要差很多。XML 可以借由 XSD 或 DTD 来定义复杂的格式,并由此来验证 XML 文档是否符合格式要求,甚至进一步的,可以基于 XSD 来生成具体语言的操作代码,例如 apache xmlbeans。并且这些工具组合到一起,形成一套庞大的生态,例如基于 XML 可以实现 SOAP 和 WSDL,一系列的 ws-*规范。但是我们也可以看到 JSON 在缺乏规范的情况下,实际上有更大一些的灵活性,特别是近年来 REST 的快速发展,已经有一些 schema 相关的发展(例如理解 JSON Schema,使用 JSON Schema, 在线 schema 测试),也有类似于 WSDL 的WADL出现。
JSON 工具
使用 JSON 实现 path 查询操作(类似 XML-PATH):JsonPATH
在线查询工具:JsonPATH、 json.cn
格式化工具:jsbeautifier
chrome 插件:5 个 Json View 插件
Java JSON 库
Java 中比较流行的 JSON 库有:
- Fastjson – 阿里巴巴开发的 JSON 库,性能十分优秀。
- Jackson – 社区十分活跃且更新速度很快。Spring 框架默认 JSON 库。
- Gson – 谷歌开发的 JSON 库,目前功能最全的 JSON 库 。
从性能上来看,一般情况下:Fastjson > Jackson > Gson
JSON 编码指南
遵循好的设计与编码风格,能提前解决 80%的问题,推荐 Google JSON 风格指南。
- 英文版Google JSON Style Guide:https://google.github.io/styleguide/jsoncstyleguide.xml
- 中文版Google JSON 风格指南:https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md
简单摘录如下:
- 属性名和值都是用双引号,不要把注释写到对象里面,对象数据要简洁
- 不要随意结构化分组对象,推荐是用扁平化方式,层次不要太复杂
- 命名方式要有意义,比如单复数表示
- 驼峰式命名,遵循 Bean 规范
- 使用版本来控制变更冲突
- 对于一些关键字,不要拿来做 key
- 如果一个属性是可选的或者包含空值或 null 值,考虑从 JSON 中去掉该属性,除非它的存在有很强的语义原因
- 序列化枚举类型时,使用 name 而不是 value
- 日期要用标准格式处理
- 设计好通用的分页参数
- 设计好异常处理
JSON API与 Google JSON 风格指南有很多可以相互参照之处。
JSON API是数据交互规范,用以定义客户端如何获取与修改资源,以及服务器如何响应对应请求。
JSON API 设计用来最小化请求的数量,以及客户端与服务器间传输的数据量。在高效实现的同时,无需牺牲可读性、灵活性和可发现性。
Fastjson 应用
添加 maven 依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>x.x.x</version></dependency>
Fastjson API
定义 Bean
Group.java
public class Group { private Long id; private String name; private List<User> users = new ArrayList<User>();}
User.java
public class User { private Long id; private String name;}
初始化 Bean
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);
序列化
String jsonString = JSON.toJSONString(group);System.out.println(jsonString);
反序列化
Group bean = JSON.parseObject(jsonString, Group.class);
Fastjson 注解
@JSONField
扩展阅读:更多 API 使用细节可以参考:JSONField 用法,这里介绍基本用法。
可以配置在属性(setter、getter)和字段(若属性是私有的,必须有set*方法。否则无法反序列化)上。
@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
- 子类型处理:SeeAlso
JSONType.alphabetic
属性: fastjson 缺省时会使用字母序序列化,如果你是希望按照 java
fields/getters
的自然顺序序列化,可以配置 JSONType.alphabetic
,使用方法如下:
@JSONType(alphabetic = false)public static class B { public int f2; public int f1; public int f0;}
Jackson 应用
扩展阅读:更多 API 使用细节可以参考 jackson-databind 官方说明
添加 maven 依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version></dependency>
Jackson API
序列化
ObjectMapper mapper = new ObjectMapper();mapper.writeValue(new File("result.json"), myResultObject);// or:byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);// or:String jsonString = mapper.writeValueAsString(myResultObject);
反序列化
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);
容器的序列化和反序列化
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();}
Jackson 注解
扩展阅读:更多注解使用细节可以参考 jackson-annotations 官方说明
@JsonProperty
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
// 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
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。
@JsonPropertyOrder(alphabetic = true)public class JacksonAnnotationBean {}
Gson 应用
详细内容可以参考官方文档:Gson 用户指南
添加 maven 依赖
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version></dependency>
Gson API
序列化
Gson gson = new Gson();gson.toJson(1); // ==> 1gson.toJson("abcd"); // ==> "abcd"gson.toJson(10L); // ==> 10int[] values = { 1 };gson.toJson(values); // ==> [1]
反序列化
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
来定制实例化,以控制其序列化、反序列化行为。
Gson gson = new GsonBuilder() .setPrettyPrinting() .setDateFormat("yyyy-MM-dd HH:mm:ss") .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) .create();
Gson 注解
@Since
@Since
用于控制对象的序列化版本。示例:
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
用于将类成员按照指定名称序列化、反序列化。示例:
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; }}
参考资料
- 官方
- Fastjson Github
- Gson Github
- jackson 官方文档
- jackson-databind
- 文章
- http://www.json.org/json-zh.html
- json 的 RFC 文档
- JSON 最佳实践
- 【简明教程】JSON