JSON(JavaScript Object Notation)是一种基于文本的数据交换格式。几乎所有的编程语言都有很好的库或第三方工具来提供基于 JSON 的 API 支持,因此你可以非常方便地使用任何自己喜欢的编程语言来处理 JSON 数据。
本文主要从 Java 语言的角度来讲解 JSON 的应用。
1. JSON 简介
JSON 是什么
JSON 起源于 1999 年的 JS 语言规范 ECMA262 的一个子集 (即 15.12 章节描述了格式与解析),后来 2003 年作为一个数据格式ECMA404 (很囧的序号有不有?)发布。 2006 年,作为 rfc4627 发布,这时规范增加到 18 页,去掉没用的部分,十页不到。
JSON 的应用很广泛,这里有超过 100 种语言下的 JSON 库:json.org 。
更多的可以参考这里,关于 json 的一切 。
优缺点、标准与 schema
结构与类型
这估计是最简单标准规范之一:
只有两种结构:对象内的键值对集合结构和数组,对象用 {}
表示、内部是”key”:”value”,数组用 []
表示,不同值用逗号分开
基本数值有 7 个: false / null / true / object / array / number / string
Copy {
"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 ]
}
}
扩展阅读:
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 ,使用 JSON Schema , 在线 schema 测试 ),也有类似于 WSDL 的WADL 出现。
相关技术以及与 XML 的关系
使用 JSON 实现 path 查询操作(类似 XML-PATH):JsonPATH
工具
1.2. Java JSON 库
Java 中比较流行的 JSON 库有:
Jackson - 社区十分活跃且更新速度很快。Spring 框架默认 JSON 库。
Gson - 谷歌开发的 JSON 库,目前功能最全的 JSON 库 。
从性能上来看,一般情况下:Fastjson > Jackson > Gson
JSON 编码指南
4.1 Google JSON 风格指南
遵循好的设计与编码风格,能提前解决 80%的问题:
简单摘录如下:
属性名和值都是用双引号,不要把注释写到对象里面,对象数据要简洁
不要随意结构化分组对象,推荐是用扁平化方式,层次不要太复杂
如果一个属性是可选的或者包含空值或 null 值,考虑从 JSON 中去掉该属性,除非它的存在有很强的语义原因
序列化枚举类型时,使用 name 而不是 value
4.2 使用 JSON 实现 API
JSON API 与 Google JSON 风格指南有很多可以相互参照之处。
JSON API 是数据交互规范,用以定义客户端如何获取与修改资源,以及服务器如何响应对应请求。
JSON API 设计用来最小化请求的数量,以及客户端与服务器间传输的数据量。在高效实现的同时,无需牺牲可读性、灵活性和可发现性。
2. Fastjson 应用
扩展阅读:更多 API 使用细节可以参考:JSONField 用法
2.1. 添加 maven 依赖
Copy <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>
2.2. Fastjson API
定义 Bean
Group.java
Copy 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
Copy 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
Copy 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);
序列化
Copy String jsonString = JSON . toJSONString (group);
System . out . println (jsonString);
反序列化
Copy Group bean = JSON . parseObject (jsonString , Group . class );
2.3. Fastjson 注解
@JSONField
扩展阅读:更多 API 使用细节可以参考:JSONField 用法 ,这里介绍基本用法。
可以配置在属性(setter、getter)和字段(必须是 public field)上。
Copy @ 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
JSONType.alphabetic 属性: fastjson 缺省时会使用字母序序列化,如果你是希望按照 java fields/getters 的自然顺序序列化,可以配置 JSONType.alphabetic,使用方法如下:
Copy @ JSONType (alphabetic = false )
public static class B {
public int f2;
public int f1;
public int f0;
}
3. Jackson 应用
扩展阅读:更多 API 使用细节可以参考 jackson-databind 官方说明
3.1. 添加 maven 依赖
Copy <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
3.2. Jackson API
序列化
Copy ObjectMapper mapper = new ObjectMapper() ;
mapper . writeValue ( new File( "result.json" ) , myResultObject);
// or:
byte [] jsonBytes = mapper . writeValueAsBytes (myResultObject);
// or:
String jsonString = mapper . writeValueAsString (myResultObject);
反序列化
Copy 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 );
容器的序列化和反序列化
Copy 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 官方说明
@JsonProperty
Copy 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
Copy // 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
Copy 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。
Copy @ JsonPropertyOrder (alphabetic = true )
public class JacksonAnnotationBean {}
4. Gson 应用
详细内容可以参考官方文档:Gson 用户指南
4.1. 添加 maven 依赖
Copy <dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
4.2. Gson API
序列化
Copy Gson gson = new Gson() ;
gson . toJson ( 1 ); // ==> 1
gson . toJson ( "abcd" ); // ==> "abcd"
gson . toJson ( 10L ); // ==> 10
int [] values = { 1 };
gson . toJson (values); // ==> [1]
反序列化
Copy 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
来定制实例化,以控制其序列化、反序列化行为。
Copy 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
用于控制对象的序列化版本。示例:
Copy public class VersionedClass {
@ Since ( 1.1 ) private final String newerField;
@ Since ( 1.0 ) private final String newField;
private final String field;
public VersionedClass () {
this . new erField = "newer" ;
this . new Field = "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
用于将类成员按照指定名称序列化、反序列化。示例:
Copy 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
6. 参考资料