JSON序列化方案
什么是Jackson:
- Jackson是比较主流的基于Java的JSON类库,可用于Json和XML与JavaBean之间的序列化和反序列化。
- 没看错,Jackson也可以处理JavaBean与XML之间的转换,基于jackson-dataformat-xml组件,而且比较JDK自带XML实现更加高效和安全。而我们使用比较多的是处理JSON与JavaBean之间的功能。
- Jackson主流到什么程度?单从Maven仓库中的统计来看,Jackson的使用量排位第一。而Spring
Boot支持的三个JSON库(Gson、Jackson、JSON-B)中,Jackson是首选默认库。 - Jackson也有以下特点:依赖少,简单易用,解析大Json速度快、内存占用比较低、拥有灵活的API、方便扩展与定制。
- Jackson的pom依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.2</version>
</dependency>
- Jackson基本使用:
①Jackson核心类ObjectMapper
②Jackson提供了三种JSON的处理方式,分别是:数据绑定、JSON树模型、流式API。
③其中前两项功能都是基于ObjectMapper来实现的,
④而流式API功能则需要基于更底层的JsonGenerator和JsonParser来实现。
FastJson为何:
- 官网的介绍:FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java
Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。 - FastJson是Java程序员常用到的类库之一,相信点开这个页面的你,也肯定是程序员朋友。正如其名,“快”是其主要卖点。
- FastJson的pom依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
- Fastjson中使用介绍:
一、Fastjson中的经常调用的方法:
1 public static final Object parse(String text); // 把JSON文本parse为JSONObject或者JSONArray
2 public static final JSONObject parseObject(String text); // 把JSON文本parse成JSONObject
3 public static final T parseObject(String text, Class clazz); // 把JSON文本parse为JavaBean
4 public static final JSONArray parseArray(String text); // 把JSON文本parse成JSONArray
5 public static final List parseArray(String text, Class clazz); //把JSON文本parse成JavaBean集合
6 public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本
7 public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本
8 public static final Object toJSON(Object javaObject); //将JavaBean转换为JSONObject或者JSONArray。
二、Fastjson字符串转List<Map<String,Object>>(), 或者List<String>()的用法:
List<Map<String, Object>> list = JSONObject.parseObject(respJson, new TypeReference<List<Map<String, Object>>>() {});
三、Fastjson的SerializerFeature序列化属性:
QuoteFieldNames———-输出key时是否使用双引号,默认为true
WriteMapNullValue——–是否输出值为null的字段,默认为false
WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
例如:JSON.toJSONString(resultMap, SerializerFeature.WriteMapNullValue);
GSON介绍:
-
GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。
-
特点:
①快速、高效
②代码量少、简洁
③面向对象
④数据传递和解析方便 -
Gson的pom依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
- Google的Gson包的使用简介
①Gson类:解析json的最基础的工具类
②JsonParser类:解析器来解析JSON到JsonElements的解析树
③JsonElement类:一个类代表的JSON元素
④JsonObject类:JSON对象类型
⑤JsonArray类:JsonObject数组
⑥TypeToken类:用于创建type,比如泛型List<?>
Hessian序列化:
Hessian序列化的前提:Hessian要实现序列化,前提是被序列化的类得实现Serializable接口。
- 注意:
①静态属性不能被序列化;
②transient关键字修饰的属性不能被序列化; - Hessian序列化与Java默认的序列化区别?
①Hessian 支持跨语言串行
②比java序列化具有更好的性能和易用性
③支持的语言比较多 - maven依赖:
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.7</version>
</dependency>
方案对比
为什么不使用fastjson?
- 你写个bean,然后属性里分别有包含_(下划线开头、#开头)之类的属性,序列化为json时,出现属性丢失,那么自然你也无法反序列化回来,据说最新的1.2.14已经改正了这个bug(我没测试不做评价);
- 翻阅fastjson的源码,你会发现有很多写死的代码,比如:针对spring之类的框架的各种处理,都是用classload判断是否存在这种类名fastjson/SerializeConfig.java at master · alibaba/fastjson GitHub;那么这是什么意思呢?意思就是如果你用spring的那种思想,自己写了个类似的功能,因为你这个项目里没有spring的那个类,那么用起来就有一堆bug;当然不仅限于这些,还有很多,比如ASM字节码织入部分fastjson/ASMSerializerFactory.java at master · alibaba/fastjson ·GitHub(温少的ASM方面水平是个半吊子),看源码的话,能发现的缺点数不胜数。
- 其解析json主要是用的String类substring这个方法fastjson/JSONScanner.java at master ·alibaba/fastjson GitHub,所以解析起来非常“快”,因为申请内存次数很少。但是因为jdk1.7之前substring的实现并没有new一个新对象,在使用的时候,如果解析的json非常多,稍不注意就会出现内存泄漏(比如一个40K的json,你在对象里引用了里边的一个key,即使这个key只有2字节,也会导致这40K的json无法被垃圾回收器回收),这也是“快”带来的负面效果。而且这还不算,在jdk1.7以上版本对string的substring方法做了改写,改成了重新new一个string的方式,于是这个“快”的优势也不存在了。总结:fastjson就是一个代码质量较差的国产类库,用很多投机取巧的的做法去实现所谓的“快”,而失去了原本应该兼容的java特性,对json标准遵循也不严格,自然很难在国际上流行。
- 链接:fastjson这么快老外为啥还是热衷 jackson
Jackson Vs.Gson:
- 测试结果:
数据集 | gson耗时 | Jackson耗时 |
---|---|---|
10w | 1366 | 138 |
20w | 2720 | 165 |
30w | 4706 | 332 |
40w | 9526 | 317 |
50w | 本机OOM | 363 |
Jackson是世界最好的JSON库:
- Jackson是一个简单的、功能强大的、基于Java的应用库。
- 它可以很方便完成Java对象和Json对象(xml文档or其它格式)进行互转。
- Jackson社区相对比较活跃,更新速度也比较快。
- Jackson库有如下几大特性:
①高性能且稳定:低内存占用,对大/小JSON串,大/小对象的解析表现均很优秀
②流行度高:是很多流行框架的默认选择
③容易使用:提供高层次的API,极大简化了日常使用案例
④无需自己手动创建映射:内置了绝大部分序列化时和Java类型的映射关系
⑤干净的JSON:创建的JSON具有干净、紧凑、体积小等特点
⑥第三方依赖:仅依赖于JDK
⑦Spring生态加持:jackson是Spring家族的默认JSON/XML解析器(明白了吧,学完此专栏你对Spring都能更亲近些了,一举两得)
org.json与json-lib的区别:
- org.json 是JSON国际组织官方推出的标准json解析方案,已经被 android sdk纳入到标准内置类库,依赖项少,但直至API17版本SDK中,仅支持JSONObject与JSONArray、Map、List、String、Boolean、Integer等基本类型对象,适合简单开发调用。
- json-lib 是另外一个开源项目,需要自行下载,依赖项较多,除org.json所支持的对象外,还直接支持基本类型数组、对象数组、json<->xml 格式转换、json<->自定义Class(Bean) 转换等功能,适合复杂的扩展调用开发。
调查资料发现,json-lib比较老旧,由于自身的众多bug、依赖多、API繁琐、处理效率低下等问题,官方已经停止维护并逐渐被淘汰。转而一个称为jackson的项目,因为效率高、依赖少,社区活跃,文档齐全,很快成为替代json-lib的主流。
Jackson简介
Jackson三大核心模块:
- core module(核心模块) 是扩展模块构建的基础。Jackson目前有3个核心模块:
①说明:核心模块的groupId均为:<groupId>com.fasterxml.jackson.core</groupId>,artifactId见下面各模块所示 Streaming流处理模块(jackson-core):定义底层处理流的API:JsonPaser和JsonGenerator等,并包含特定于json的实现。
Annotations标准注解模块(jackson-annotations):包含标准的Jackson注解
Databind数据绑定模块(jackson-databind):在streaming包上实现数据绑定(和对象序列化)支持;它依赖于上面的两个模块,也是Jackson的高层API(如ObjectMapper)所在的模块
实际应用级开发中,我们只会使用到Databind数据绑定模块
,所以它是本系列重中之重。- 链接:史上最全的Jackson框架使用教程
使用 Jackson 的核心模块的 jar 包需要在 pom.xml 中添加如下信息:
jackson-databind 依赖 jackson-core 和 jackson-annotations
- 当添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也随之添加到Java 项目工程中。在添加相关依赖包之后,就可以使用 Jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
Jackson使用
序列化和反序列化简单使用案例:
Jackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper。
下面是一个 ObjectMapper的使用的简单示例。- 准备一个名称为 Person 的 Java 对象:
public class Person {
// 正常case
private String name;
// 空对象case
private Integer age;
// 日期转换case
private Date date;
// 默认值case
private int height;
}
- 使用示例:
@Test
public void test1() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// 造数据
Person person = new Person();
person.setName("Tom");
person.setAge(40);
person.setDate(new Date());
System.out.println("序列化");
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
System.out.println(jsonString);
System.out.println("反序列化");
Person deserializedPerson = mapper.readValue(jsonString, Person.class);
System.out.println(deserializedPerson);
}
- 打印输出:
序列化:
{
"name" : "Tom",
"age" : 40,
"date" : 1594634846647,
"man" : null,
"height" : 0
}
反序列化:
JackSonTest.Person(name=Tom, age=40,
date=Mon Jul 13 18:07:26 CST 2020, man=null, height=0)
ObjectMapper 通过 writeValue 系列方法将 java 对象序列化为 json,并将 json存储成不同的格式,String(writeValueAsString),ByteArray(writeValueAsString),Writer, File,OutStream 和 DataOutput。
ObjectMapper 通过 readValue 系列方法从不同的数据源像 String , Byte Array,Reader,File,URL, InputStream 将 json 反序列化为 java 对象。
统一配置
简介:
在调用 writeValue 或调用 readValue 方法之前,往往需要设置 ObjectMapper的相关配置信息。这些配置信息应用 java 对象的所有属性上
。示例如下:
//在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//在序列化时自定义时间日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//在序列化时忽略值为 null 的属性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//在序列化时忽略值为默认值的属性
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
- 更多配置信息可以查看 Jackson 的 DeserializationFeature,SerializationFeature 和
Include。 - 重新运行单元测试,打印输出:
序列化:
{
"name" : "Tom",
"age" : 40,
"date" : "2020-07-26 18:46:51"
}
反序列化:
JackSonTest.Person(name=Tom, age=40, date=Sun Jul 26 18:46:51 CST 2020, height=0)
使用注解
简介:
Jackson 根据它的默认方式序列化和反序列化 java 对象,若根据实际需要,灵活的调整它的默认方式,可以使用 Jackson的注解
。常用的注解及用法如下:
注解 | 用法 |
---|---|
@JsonProperty | 用于属性,把属性的名称序列化时转换为另外一个名称。示例:@JsonProperty(“birth_date”) private Date birthDate |
@JsonIgnore | 可用于字段、getter/setter、构造函数参数上,作用相同,都会对相应的字段产生影响。使相应字段不参与序列化和反序列化。 |
@JsonIgnoreProperties | 该注解是类注解。该注解在Java类和JSON不完全匹配的时候使用。 |
@JsonFormat | 用于属性或者方法,把属性的格式序列化时转换成指定的格式。示例:@JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm”) public Date getBirthDate() |
@JsonPropertyOrder | 用于类, 和 @JsonProperty 的index属性类似,指定属性在序列化时 json 中的顺序 , 示例:@JsonPropertyOrder({ “birth_Date”, “name” }) public class Person |
@JsonCreator | 用于构造方法,和 @JsonProperty 配合使用,适用有参数的构造方法。示例:@JsonCreator public Person(@JsonProperty(“name”)String name) {…} |
@JsonAnySetter | 用于属性或者方法,设置未反序列化的属性名和值作为键值存储到 map 中 @JsonAnySetter public void set(String key, Object value) { map.put(key, value); } |
@JsonAnyGetter | 用于方法 ,获取所有未序列化的属性 public Map<String, Object> any() { return map; } |
@JsonNaming | 类注解。序列化的时候该注解可将驼峰命名的字段名转换为下划线分隔的小写字母命名方式。反序列化的时候可以将下划线分隔的小写字母转换为驼峰命名的字段名。示例:@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
@JsonRootName | 类注解。需开启mapper.enable(SerializationFeature.WRAP_ROOT_VALUE),用于序列化时输出带有根属性名称的 JSON 串,形式如 {“root_name”:{“id”:1,“name”:“zhangsan”}}。但不支持该 JSON 串反序列化。 |
- 使用示例:
// 用于类,指定属性在序列化时 json 中的顺序
@JsonPropertyOrder({"date", "user_name"})
// 批量忽略属性,不进行序列化
@JsonIgnoreProperties(value = {"other"})
// 用于序列化与反序列化时的驼峰命名与小写字母命名转换
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public static class User {
@JsonIgnore
private Map<String, Object> other = new HashMap<>();
// 正常case
@JsonProperty("user_name")
private String userName;
// 空对象case
private Integer age;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
// 日期转换case
private Date date;
// 默认值case
private int height;
public User() {
}
// 反序列化执行构造方法
@JsonCreator
public User(@JsonProperty("user_name") String userName) {
System.out.println("@JsonCreator 注解使得反序列化自动执行该构造方法 " + userName);
// 反序列化需要手动赋值
this.userName = userName;
}
@JsonAnySetter
public void set(String key, Object value) {
other.put(key, value);
}
@JsonAnyGetter
public Map<String, Object> any() {
return other;
}
// 本文默认省略getter、setter方法
}
- 单元测试:
@Test
public void test3() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// 造数据
Map<String, Object> map = new HashMap<