JSON 简介
JSON 是什么
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言:JSON 使用 Javascript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。目前非常多的动态(PHP,JSP,.NET)编程语言都支持 JSON
- JSON 具有自我描述性,更易理解
结构与类型
- 只有两种结构:对象内的键值对集合结构和数组,对象用
{}表示、内部是"key":"value",数组用[]表示,不同值用逗号分开 - 基本数值有 7 个:
false/null/true/object/array/number/string - 再加上结构可以嵌套,进而可以用来表达复杂的数据
一个简单实例:
{
"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]
}
}
JSON 优秀资源
JSON 在线解析工具
JSON 类库
Java 中并没有内置 JSON 的解析,因此使用 JSON 需要借助第三方类库。下面是几个常用的 JSON 解析类库:
- FastJson:阿里巴巴开发的 JSON 库,性能优秀
- Jackson:社区十分活跃且更新速度很快
- Gson:谷歌开发的 JSON 库,功能十分全面
性能测试对比
从下面的测试结果可以看出,序列化次数比较小的时候,Gson 性能最好,当不断增加的时候到了 100000,Gson 明显弱于 Jackson 和 FastJson,这时候 FastJson 性能是真的牛,另外还可以看到不管数量少还是多,Jackson 一直表现优异。而那个 Json-lib 可以直接忽略。
- JSON 序列化性能(测试图表参见原文)
- JSON 反序列化性能(测试图表参见原文)
更多请参考:Java 几种常用 JSON 库性能比较。
FastJson
先泼一盆冷水,个人非常不推荐使用 FastJson,为什么?
- FastJson 源码质量较低
- FastJson Bug、漏洞较多
- FastJson 牺牲多数场景下的稳定性而提高的效率
Fastjson 简介
Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。
Fastjson 特性
- 提供服务器端、安卓客户端两种解析工具,性能表现较好
- 提供了
toJSONString()和parseObject()方法来将 Java 对象与 JSON 相互转换。调用 toJSONString 方法即可将对象转换成 JSON 字符串,parseObject 方法则反过来将 JSON 字符串转换成对象 - 允许转换预先存在的无法修改的对象(只有 class、无源代码)
- Java 泛型的广泛支持
- 允许对象的自定义表示、允许自定义序列化类
- 支持任意复杂对象(具有深厚的继承层次和广泛使用的泛型类型)
下载和使用
你可以在 maven 中央仓库中直接下载:http://repo1.maven.org/maven2/com/alibaba/fastjson/
配置 maven 依赖,其中 x.x.x 是版本号,根据需要使用特定版本,建议使用最新版本:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>
序列化一个对象成 JSON 字符串
User user = new User();
user.setName("校长");
user.setAge(3);
user.setSalary(new BigDecimal("123456789.0123"));
String jsonString = JSON.toJSONString(user);
System.out.println(jsonString);
// 输出 {"age":3,"name":"校长","old":false,"salary":123456789.0123}
反序列化一个 JSON 字符串成 Java 对象
String jsonString = "{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}";
User u = JSON.parseObject(jsonString, User.class);
System.out.println(u.getName());
// 输出 校长
String jsonStringArray = "[{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}]";
List<User> userList = JSON.parseArray(jsonStringArray, User.class);
System.out.println(userList.size());
// 输出 1
对于日期的处理
默认序列化 Date 输出使用 "yyyy-MM-dd HH:mm:ss" 格式,可以用 UseISO8601DateFormat 特性换成 "yyyy-MM-dd'T'HH:mm:ss" 格式。
JSON.defaultTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
JSON.defaultLocale = Locale.US;
public static class Model {
@JSONField(format = "MMM dd, yyyy h:mm:ss aa")
private java.util.Date date;
public java.util.Date getDate() {
return date;
}
public void setDate(java.util.Date date) {
this.date = date;
}
@JSONField(format = "MMM-dd-yyyy h:mm:ss aa")
public java.sql.Date date2;
}
Bean 和数组转换
官方例子:BeanToArray_cn
设置字段名
public class A {
@JSONField(name = "ID")
private int id;
public int getId() { return id; }
public void setId(int value) { this.id = id; }
}
设置是否不序列化某字段
public class A {
@JSONField(serialize = false)
public Date date;
}
public class A {
@JSONField(deserialize = false)
public Date date;
}
设置字段顺序
public static class VO {
@JSONField(ordinal = 3)
private int f0;
@JSONField(ordinal = 2)
private int f1;
@JSONField(ordinal = 1)
private int f2;
}
自定义序列化和反序列化
FastJson 漏洞问题
- 尽量使用最新版本
- fastjson 远程代码执行漏洞技术分析与防护方案
好了,我要开喷了。
警告:远离 FastJson 这个库,老程序员都知道这里有多少坑:
- alibaba/fastjson 的 issue 列表
- fastjson 这么快老外为啥还是热衷 jackson?
JackSon
JackSon 简介
Jackson 组件
3 个核心模块:
- Streaming:jackson-core jar,定义了底层的 streaming API 和实现了 Json 特性
- Annotations:jackson-annotations jar,包含了标准的 Jackson 注解(本文暂不介绍)
- Databind:jackson-databind jar,实现了数据绑定和对象序列化,它依赖于 streaming 和 annotations 的包
第三方数据类型模块:这些扩展是插件式的 Jackson 模块,用 ObjectMapper.registerModule() 注册,并且通过添加 serializers 和 deserializers 以便 Databind 包(ObjectMapper / ObjectReader / ObjectWriter)可以读写这些类型,来增加对各种常用的 Java 库的数据类型的支持。
数据格式模块:Jackson 也有处理程序对 JAX-RS 标准实现者例如 Jersey、RESTeasy、CXF 等提供了数据格式支持。处理程序实现了 MessageBodyReader 和 MessageBodyWriter,目前支持的数据格式包括 JSON、Smile、XML、YAML 和 CBOR。数据格式提供了除了 Json 之外的数据格式支持,它们绝大部分仅仅实现了 streaming API abstractions,以便数据绑定组件可以按照原来的方式使用。另一些(几乎不需要)提供了 databind 标准功能来处理例如 schemas。
Jackson 的使用
引用 maven jar 包:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.1</version>
</dependency>
序列化一个对象成 JSON 字符串
public void toJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
City case1 = new City();
case1.setCity("SZ");
case1.setAge(123);
String jsonStr = mapper.writeValueAsString(case1);
System.out.println("JSON:" + jsonStr);
}
// 输出:JSON:{"city":"SZ","age":123}
反序列化一个 JSON 字符串成 Java 对象
public void toObj() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
String inputjsonstr = "{\"city\":\"SZ\",\"age\":123}";
City readcase = mapper.readValue(inputjsonstr, City.class);
System.out.println("city info:" + readcase);
}
如果里面有未知属性,比如 json 中有 desc 字段,但是 City 中没有相应字段,会报错,需要设置如下:
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
常用注解
@JsonProperty("xxx"):将当前的属性名在 json 字符串中重新命名为当前设置的这个值,比如在示例中,将 age —> mAge@JsonIgnore:将被标注的属性在生成 json 字符串的时候,直接忽略@JsonInclude:是一个类级别的设置,JsonInclude.Include.NON_EMPTY 标识只有非 NULL 的值才会被纳入 json string 之中,其余的都被忽略,比如这里的 location 属性,并没有出现在最终的结果字符串中@JsonSerialize:使用自定义的类来实现自定义的字段转换。写入操作@JsonDeserialize:解析的时候,自定义的转换器;读取操作@JsonAutoDetect:设置类的访问策略,是否所有的属性都可以,还是按照一定的方式来提取@JsonRawValue:无转换的将属性值写入到 json 字符串中。写入操作@JsonValue:标注方法,用以替代缺省的方法,由该方法来完成 json 的字符输出
GSON
Gson 简介
Gson 是这样一个 Java 类库,它可以将 Java 对象转换为相应的 JSON 形式,也可以将 JSON 字符串转换为对应的 Java 对象。Gson 可以使用任意 Java 对象,包括哪些预先存在的、不在你的源代码中的对象(因此,你并不知道对象的属性)。
Gson 的目标
- 提供一种机制,使得将 Java 对象转换为 JSON 或相反如使用 toString() 以及构造器(工厂方法)一样简单
- 允许预先存在的不可变的对象转换为 JSON 或与之相反
- 允许自定义对象的表现形式
- 支持任意复杂的对象
- 输出轻量易读的 JSON
Gson 的使用
使用 Gson 的首要类是 Gson 类,你可以仅仅通过 new Gson() 的方式创建它。你也可以通过 GsonBuilder 类去创建 Gson 实例,这个类允许你进行一系列配置,例如版本控制等等。
Gson 实例不会保存任何进行 Json 操作时的状态。因此,你可以自由的复用相同的 Gson 对象进行诸多的 Json 序列化和反序列化操作。
引用 maven jar 包(原 PDF 此处误贴了 guava 的依赖坐标,已更正为 gson):
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
序列化
基础类型:
Gson gson = new Gson();
gson.toJson(1); // ==> prints 1
gson.toJson("abcd"); // ==> prints "abcd"
gson.toJson(new Long(10)); // ==> prints 10
int[] values = {1};
gson.toJson(values); // ==> prints [1]
对象:
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
// ==> json is {"value1":1,"value2":"abc"}
其中的对象代码:
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
数组:
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
gson.toJson(ints); // ==> prints [1,2,3,4,5]
gson.toJson(strings); // ==> prints ["abc","def","ghi"]
集合:
Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1, 2, 3, 4, 5);
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
反序列化
基础类型:
Gson gson = new Gson();
int one = gson.fromJson("1", int.class);
Integer one2 = gson.fromJson("1", Integer.class);
Long one3 = gson.fromJson("1", Long.class);
Boolean fals = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);
对象:
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 is just like obj
数组:
Gson gson = new Gson();
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
// ==> ints2 will be same as ints
集合:
Gson gson = new Gson();
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints
自定义序列化和反序列化机制
有时候,默认的实现并不是你想要的。这在处理类库时常常发生(例如 DateTime)。Gson 允许你注册自己自定义的序列化器和反序列化器。该过程分为三部分:
- Json 序列化器:需要为一个对象自定义序列化机制
- Json 反序列化器:需要为一个类型自定义反序列化机制
- 实例构造者:并不需要,如果无参构造器是可用的或者注册了一个反序列化器
registerTypeAdapter 会检查类型适配器是否实现了上面三个接口中的一个以上并且它们都注册了类型适配器:
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
更多请参考:Gson 用户指南(中文翻译)
参考文章
- https://github.com/FasterXML/jackson
- https://www.runoob.com/w3cnote/fastjson-intro.html
- https://blog.csdn.net/m0_37076574/article/details/81317403
- https://blog.csdn.net/blueheart20/article/details/52212221
- https://blog.csdn.net/gjb724332682/article/details/51586701
- https://www.jianshu.com/p/1e20b28c39d1
- https://www.jianshu.com/p/923a9fe78108