JSON序列化方案知识概括

403 阅读18分钟

@TOC

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耗时
10w1366138
20w2720165
30w4706332
40w9526317
50w本机OOM363

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<>();
    map.put("user_name", "Tom");
    map.put("date", "2020-07-26 19:28:44");
    map.put("age", 100);
    map.put("demoKey", "demoValue");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
    System.out.println(jsonString);
    System.out.println("反序列化");
    User user = mapper.readValue(jsonString, User.class);
    System.out.println(user);
    System.out.println("序列化");
    jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
    System.out.println(jsonString);
}
  • 打印输出:
{
  "date" : "2020-07-26 19:28:44",
  "demoKey" : "demoValue",
  "user_name" : "Tom",
  "age" : 100
}
反序列化
@JsonCreator 注解使得反序列化自动执行该构造方法Tom
JackSonTest.User(other={demoKey=demoValue}, userName=Tom, age=100, date=Sun Jul 26 19:28:44 CST 2020, height=0)
序列化
{
  "date" : "2020-07-26 19:28:44",
  "user_name" : "Tom",
  "age" : 100,
  "height" : 0,
  "demoKey" : "demoValue"
}

日期处理

简介:

  • 不同类型的日期类型,JackSon 的处理方式也不同。

  • 普通日期 ①对于日期类型为 java.util.Calendar, java.util.GregorianCalendar, java.sql.Date, java.util.Date, java.sql.Timestamp,若不指定格式,在 json 文件中将序列化为 long 类型的数据。显然这种默认格式,可读性差,转换格式是必要的。 ②JackSon 有很多方式转换日期格式。 <1>注解方式,使用 @JsonFormat 注解指定日期格式。 <2>ObjectMapper 方式,调用 ObjectMapper 的方法 setDateFormat,将序列化为指定格式的 string 类型的数据。

  • Local日期 ①对于日期类型为 java.time.LocalDate, java.time.LocalDateTime,还需要添加代码 mapper.registerModule(new JavaTimeModule()),同时添加相应的依赖 jar 包。 ②导入依赖: <1>对于 Jackson 2.5 以下版本,需要添加代码 mapper.registerModule(new JSR310Module ())。

<dependency> 
  <groupId>com.fasterxml.jackson.datatype</groupId> 
  <artifactId>jackson-datatype-jsr310</artifactId> 
  <version>2.9.1</version> 
</dependency>
-----------------------使用示例:--------------------------
@Data
public static class Student {
    // 正常case
    private String name;
    // 日期转换case
    private LocalDateTime date;
}

@Test
public void test4() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 必须添加对LocalDate的支持
    mapper.registerModule(JavaTimeModule());
    // 造数据
    Student student = new Student();
    student.setName("Tom");
    student.setDate(LocalDateTime.now());
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);
    System.out.println(jsonString);
    System.out.println("反序列化");
    Student deserializedPerson = mapper.readValue(jsonString, Student.class);
    System.out.println(deserializedPerson);
}

private Module JavaTimeModule() {
    JavaTimeModule module = new JavaTimeModule();
    String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    String DATE_FORMAT = "yyyy-MM-dd";
    String TIME_FORMAT = "HH:mm:ss";
    module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
    module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    module.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern((TIME_FORMAT))));
    module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
    return module;
}
-----------------------打印输出:--------------------------
序列化
{
  "name" : "Tom",
  "date" : "2020-07-26 23:10:42"
}
反序列化
JackSonTest.Student(name=Tom, date=2020-07-26T23:10:42)
  • Joda日期 ①对于日期类型为 org.joda.time.DateTime,还需要添加代码 mapper.registerModule(new JodaModule()),同时添加相应的依赖 jar 包
<dependency> 
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId> 
  <version>2.9.1</version> 
</dependency>

对象集合

简介:

  • Jackson 对泛型反序列化也提供很好的支持。
  • List ①对于 List 类型 ,可以调用 constructCollectionType 方法来序列化,也可以构造 TypeReference 来序列化。
-----------------------使用示例:-----------------------
@Test
public void test5() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    CollectionType javaType = mapper.getTypeFactory().constructCollectionType(List.class, Person.class);
    // 造数据
    List<Person> list = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        Person person = new Person();
        person.setName("Tom");
        person.setAge(new Random().nextInt(100));
        person.setDate(new Date());
        list.add(person);
    }
    System.out.println("序列化");
    String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(list);
    System.out.println(jsonInString);
    System.out.println("反序列化:使用 javaType");
    List<Person> personList = mapper.readValue(jsonInString, javaType);
    System.out.println(personList);
    System.out.println("反序列化:使用 TypeReference");
    List<Person> personList2 = mapper.readValue(jsonInString, new TypeReference<List<Person>>() {
    });
    System.out.println(personList2);
}
-----------------------打印输出:-----------------------
序列化
[ {
  "name" : "Tom",
  "age" : 33,
  "date" : 1595778639371,
  "height" : 0
}, {
  "name" : "Tom",
  "age" : 3,
  "date" : 1595778639371,
  "height" : 0
}, {
  "name" : "Tom",
  "age" : 36,
  "date" : 1595778639371,
  "height" : 0
} ]
反序列化:使用javaType
[JackSonTest.Person(name=Tom, age=33, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=3, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=36, date=Sun Jul 26 23:50:39 CST 2020, height=0)]
反序列化:使用TypeReference
[JackSonTest.Person(name=Tom, age=33, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=3, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=36, date=Sun Jul 26 23:50:39 CST 2020, height=0)]
  • Map ①对于 map 类型, 与 List 的实现方式相似。
-------------------使用示例:-------------------------
@Test
public void test6() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    //第二参数是 map 的 key 的类型,第三参数是 map 的 value 的类型
    MapType javaType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Person.class);
    // 造数据
    Map<String, Person> map = new HashMap<>();
    for (int i = 0; i < 3; i++) {
        Person person = new Person();
        person.setName("Tom");
        person.setAge(new Random().nextInt(100));
        person.setDate(new Date());
        map.put("key" + i, person);
    }
    System.out.println("序列化");
    String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
    System.out.println(jsonInString);
    System.out.println("反序列化: 使用 javaType");
    Map<String, Person> personMap = mapper.readValue(jsonInString, javaType);
    System.out.println(personMap);
    System.out.println("反序列化: 使用 TypeReference");
    Map<String, Person> personMap2 = mapper.readValue(jsonInString, new TypeReference<Map<String, Person>>() {
    });
    System.out.println(personMap2);
}

----------------------打印输出:----------------------
序列化
{
  "key1" : {
    "name" : "Tom",
    "age" : 95,
    "date" : 1595778740606,
    "height" : 0
  },
  "key2" : {
    "name" : "Tom",
    "age" : 27,
    "date" : 1595778740606,
    "height" : 0
  },
  "key0" : {
    "name" : "Tom",
    "age" : 98,
    "date" : 1595778740606,
    "height" : 0
  }
}
反序列化: 使用 javaType
{key1=JackSonTest.Person(name=Tom, age=95, date=Sun Jul 26 23:52:20 CST 2020, height=0), key2=JackSonTest.Person(name=Tom, age=27, date=Sun Jul 26 23:52:20 CST 2020, height=0), key0=JackSonTest.Person(name=Tom, age=98, date=Sun Jul 26 23:52:20 CST 2020, height=0)}
反序列化: 使用 TypeReference
{key1=JackSonTest.Person(name=Tom, age=95, date=Sun Jul 26 23:52:20 CST 2020, height=0), key2=JackSonTest.Person(name=Tom, age=27, date=Sun Jul 26 23:52:20 CST 2020, height=0), key0=JackSonTest.Person(name=Tom, age=98, date=Sun Jul 26 23:52:20 CST 2020, height=0)}
  • Array 和 Collection 的处理与 List,Map 相似,这里不再详述。

属性可视化

简介:

  • JackSon 默认不是所有的属性都可以被序列化和反序列化。默认的属性可视化的规则如下: ①若该属性修饰符是 public,该属性可序列化和反序列化。 ②若属性的修饰符不是 public,但是它的 getter 方法和 setter 方法是 public,该属性可序列化和反序列化。因为 getter 方法用于序列化, 而 setter 方法用于反序列化。 ③若属性只有 public 的 setter 方法,而无 public 的 getter 方 法,该属性只能用于反序列化。
  • 若想更改默认的属性可视化的规则,需要调用 ObjectMapper 的方法 setVisibility。
  • 下面的示例使修饰符为 protected 的属性 name 也可以序列化和反序列化:
-----------------案例:---------------------
public static class People {
    public int age;
    protected String name;
}

@Test
public void test7() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // PropertyAccessor 支持的类型有 ALL,CREATOR,FIELD,GETTER,IS_GETTER,NONE,SETTER
    // Visibility 支持的类型有 ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PUBLIC,PUBLIC_ONLY
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    // 造数据
    People people = new People();
    people.name = "Tom";
    people.age = 40;
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
    System.out.println(jsonString);
    System.out.println("反序列化");
    People deserializedPerson = mapper.readValue(jsonString, People.class);
    System.out.println(deserializedPerson);
}

-------------------打印输出:-------------------
序列化
{
  "age" : 40,
  "name" : "Tom"
}
反序列化
JackSonTest.People(age=40, name=Tom)

属性过滤

简介:

  • 在将 Java 对象序列化为 json 时 ,有些属性需要过滤掉,不显示在 json 中 ,除了使用 @JsonIgnore过滤单个属性或用 @JsonIgnoreProperties 过滤多个属性之外, Jackson 还有通过代码控制的方式。
  • 使用示例:
-------------------示例:-------------------
@JsonFilter("myFilter")
public interface MyFilter {
}

@Test
public void test8() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    //设置 addMixIn
    mapper.addMixIn(Person.class, MyFilter.class);
    //调用 SimpleBeanPropertyFilter 的 serializeAllExcept 方法
    SimpleBeanPropertyFilter newFilter = SimpleBeanPropertyFilter.serializeAllExcept("age");
    //或重写 SimpleBeanPropertyFilter 的 serializeAsField 方法
    SimpleBeanPropertyFilter newFilter2 = new SimpleBeanPropertyFilter() {
        @Override
        public void serializeAsField(Object pojo, JsonGenerator jgen,
                                     SerializerProvider provider, PropertyWriter writer)
                throws Exception {
            if (!writer.getName().equals("age")) {
                writer.serializeAsField(pojo, jgen, provider);
            }
        }
    };
    //设置 FilterProvider
    FilterProvider filterProvider = new SimpleFilterProvider().addFilter("myFilter", newFilter);
    // 造数据
    Person person = new Person();
    person.setName("Tom");
    person.setAge(40); // 该属性将被忽略
    person.setDate(new Date());
    // 序列化
    String jsonString = mapper.setFilterProvider(filterProvider).writeValueAsString(person);
    System.out.println(jsonString);
}

-------------------打印输出:-------------------
{"name":"Tom","date":1595780842754}

自定义序列化类

简介:

  • 当 Jackson 默认序列化和反序列化的类不能满足实际需要,可以自定义新的序列化和反序列化的类。
  • 自定义序列化类:自定义的序列化类需要直接或间接继承 StdSerializer 或 JsonSerializer,同时需要利用 JsonGenerator 生成 json,重写方法 serialize,示例如下:
public static class CustomSerializer extends StdSerializer<Person> {
    protected CustomSerializer() {
        super(Person.class);
    }

    @Override
    public void serialize(Person person, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeStartObject();
        jgen.writeNumberField("age", person.getAge());
        jgen.writeStringField("name", person.getName());
        jgen.writeStringField("msg", "已被自定义序列化");
        jgen.writeEndObject();
    }
}
  • JsonGenerator 有多种 write 方法以支持生成复杂的类型的 json,比如 writeArray,writeTree 等 。若想单独创建 JsonGenerator,可以通过 JsonFactory() 的 createGenerator。
  • 自定义反序列化类:自定义的反序列化类需要直接或间接继承 StdDeserializer 或 StdDeserializer,同时需要利用 JsonParser 读取 json,重写方法 deserialize,示例如下:
public static class CustomDeserializer extends StdDeserializer<Person> {
    protected CustomDeserializer() {
        super(Person.class);
    }

    @Override
    public Person deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        Person person = new Person();
        int age = (Integer) ((IntNode) node.get("age")).numberValue();
        String name = node.get("name").asText();
        person.setAge(age);
        person.setName(name);
        return person;
    }
}
  • JsonParser 提供很多方法来读取 json 信息, 如 isClosed(), nextToken(),getValueAsString()等。若想单独创建 JsonParser,可以通过 JsonFactory() 的 createParser。
  • 定义好自定义序列化类和自定义反序列化类,若想在程序中调用它们,还需要注册到 ObjectMapper 的 Module,示例如下:
@Test
public void test9() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 生成 module
    SimpleModule module = new SimpleModule("myModule");
    module.addSerializer(new CustomSerializer());
    module.addDeserializer(Person.class, new CustomDeserializer());
    // 注册 module
    mapper.registerModule(module);
    // 造数据
    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);
}
  • 或者也可通过注解方式加在 java 对象的属性,方法或类上面来调用它们:
@JsonSerialize(using = CustomSerializer.class)
@JsonDeserialize(using = CustomDeserializer.class)

树模型处理

简介:

  • Jackson 也提供了树模型(tree model)来生成和解析 json。若想修改或访问 json部分属性,树模型是不错的选择。树模型由 JsonNode 节点组成。程序中常常使用 ObjectNode,ObjectNode 继承于JsonNode,示例如下:
-------------------示例:-----------------------
@Test
public void test10() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    //构建 ObjectNode
    ObjectNode personNode = mapper.createObjectNode();
    //添加/更改属性
    personNode.put("name", "Tom");
    personNode.put("age", 40);
    ObjectNode addressNode = mapper.createObjectNode();
    addressNode.put("zip", "000000");
    addressNode.put("street", "Road NanJing");
    //设置子节点
    personNode.set("address", addressNode);
    System.out.println("构建 ObjectNode:\n" + personNode.toString());
    //通过 path 查找节点
    JsonNode searchNode = personNode.path("name");
    System.out.println("查找子节点 name:\n" + searchNode.asText());
    //删除属性
    ((ObjectNode) personNode).remove("address");
    System.out.println("删除后的 ObjectNode:\n" + personNode.toString());
    //读取 json
    JsonNode rootNode = mapper.readTree(personNode.toString());
    System.out.println("Json 转 JsonNode:\n" + rootNode);
    //JsonNode 转换成 java 对象
    Person person = mapper.treeToValue(personNode, Person.class);
    System.out.println("JsonNode 转对象:\n" + person);
    //java 对象转换成 JsonNode
    JsonNode node = mapper.valueToTree(person);
    System.out.println("对象转 JsonNode:\n" + node);
}

----------------------打印输出:---------------------
构建 ObjectNode:
{"name":"Tom","age":40,"address":{"zip":"000000","street":"Road NanJing"}}
查找子节点 name:
Tom
删除后的 ObjectNode:
{"name":"Tom","age":40}
Json 转 JsonNode:
{"name":"Tom","age":40}
JsonNode 转对象:
JackSonTest.Person(name=Tom, age=40, date=null, height=0)
对象转 JsonNode:
{"name":"Tom","age":40,"date":null,"height":0}