Jackson内置了许多功能配置项,利用好这些配置项,可以简单高效的解决一些常见的序列化问题。
接下来,介绍几个常用的配置项,在序列化和反序列化时使用。
本篇内容基于Jackson 2.11.2版本,马上开始学习吧。
序列化
定义一个用于序列化的类。
public class Animal {
private String name;
private int sex;
private Integer weight;
private People owner;
public class People {
private String name;
private int age;
// 省略getter、setter方法
@Override
public String toString() {
return "People [name=" + name + ", age=" + age + "]";
}
}
// 省略getter、setter方法
@Override
public String toString() {
return "Animal [name=" + name + ", sex=" + sex + ", weight=" + weight + ", owner=" + owner + "]";
}
}
格式化输出
默认情况下,序列化后的内容,都在同一行。当内容比较长时,不便于阅读理解。
通过启用SerializationFeature.INDENT_OUTPUT缩进输出配置,可以使得内容格式化后再输出,非常友好。
/**
* 启用缩进输出,对JSON字符串进行格式化
*
* @throws JsonProcessingException
*/
@Test
public void indentOutput() throws JsonProcessingException {
Animal animal = new Animal();
animal.setName("sam");
animal.setSex(1);
animal.setWeight(100);
People owner = animal.new People();
animal.setOwner(owner);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 格式化
System.out.println(mapper.writeValueAsString(animal));
}
执行结果:
{
"name" : "sam",
"sex" : 1,
"weight" : 100,
"owner" : {
"name" : null,
"age" : 0
}
}
序列化空Bean
当序列化一个空Bean(Bean没有字段,或者没有public字段和getter方法)时,默认会抛出异常。
示例1:
public class AnimalEmpty {
}
/**
* 默认序列化空Bean时会抛出异常
*
* @throws JsonProcessingException
*/
@Test
public void defaultEmptyBean() throws JsonProcessingException {
AnimalEmpty animal = new AnimalEmpty();
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(animal));
}
示例2:
public class AnimalNoPublic {
private String name;
protected int sex;
Integer weight;
protected int getSex() {
return sex;
}
Integer getWeight() {
return weight;
}
public void setName(String name) {
this.name = name;
}
public void setSex(int sex) {
this.sex = sex;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
/**
* 默认序列化空Bean时会抛出异常
*
* @throws JsonProcessingException
*/
@Test
public void defaultNoPublicBean() throws JsonProcessingException {
AnimalNoPublic animal = new AnimalNoPublic();
animal.setName("sam");
animal.setSex(1);
animal.setWeight(100);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(animal));
}
两个示例的执行结果:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class cn.javaee.util.jackson.config.AnimalNoPublic and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
...
异常信息提示, 为了避免抛出异常,可以禁用SerializationFeature.FAIL_ON_EMPTY_BEANS配置。
/**
* 允许序列化空Bean
*
* @throws JsonProcessingException
*/
@Test
public void allowEmptyBean() throws JsonProcessingException {
AnimalNoPublic animal = new AnimalNoPublic();
animal.setName("sam");
animal.setSex(1);
animal.setWeight(100);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // 允许序列化空Bean
System.out.println(mapper.writeValueAsString(animal));
}
执行结果:
{}
日期时间戳输出
默认情况下,日期会序列化为数字类型的时间戳。
/**
* 默认日期序列化为数字类型的时间戳
*
* @throws JsonProcessingException
*/
@Test
public void defaultDate() throws JsonProcessingException {
List<Date> list = new ArrayList<>();
list.add(new Date());
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(list));
}
执行结果:
[1601995178051]
通过禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS檡,禁止将日期序列化为数字类型的时间戳。
/**
* 禁止将日期序列化为数字类型的时间戳. 默认输出格式:yyyy-MM-dd'T'HH:mm:ss.SSSX
*
* @throws JsonProcessingException
*/
@Test
public void disableTimestamp() throws JsonProcessingException {
List<Date> list = new ArrayList<>();
list.add(new Date());
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
System.out.println(mapper.writeValueAsString(list));
}
执行结果:
["2020-10-06T14:40:01.866+00:00"]
在Jackson全局配置和JsonFormat注解设置日期格式 (opens in a new tab)一讲中提到,为字段添加@JsonFormat(shape=Shape.STRING)注解后,则日期输出会变成字符串格式,这个字符串格式和本例是相同的。
反序列化
不存在的字段
默认情况下,当反序列化的字段在目标类中不存在时,会抛出异常。
/**
* 字段weight2不存在,反序列化时将会抛出异常
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void unknownProperty() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"name\":\"sam\",\"sex\":1,\"weight2\":100}";
ObjectMapper mapper = new ObjectMapper();
Animal animal = mapper.readValue(jsonString, Animal.class);
System.out.println(mapper.writeValueAsString(animal));
}
抛出异常:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "weight2" (class cn.javaee.util.jackson.config.Animal), not marked as ignorable (4 known properties: "weight", "name", "sex", "owner"])
at [Source: (String)"{"name":"sam","sex":1,"weight2":100}"; line: 1, column: 36] (through reference chain: cn.javaee.util.jackson.config.Animal["weight2"])
...
通过禁用DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES配置,在反序列化遇到未知字段时不抛出异常。
/**
* 遇到不存在的字段时不抛异常
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void allowUnknownProperty() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"name\":\"sam\",\"sex\":1,\"weight2\":100}";
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 禁用
Animal animal = mapper.readValue(jsonString, Animal.class);
System.out.println(mapper.writeValueAsString(animal));
}
执行结果:
{"name":"sam","sex":1,"weight":null,"owner":null}
空字符串映射为null值POJO对象
定义两个简单类。
public class Student {
private String name;
private int age;
// 省略getter、setter方法
}
public class Teacher {
private Student student;
// 省略getter、setter方法
}
默认情况下,如果反序列化Teacher时,传入的student值为空字符串"",那么将会抛出异常。
/**
* 抛异常
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void emptyStringToPOJO() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"student\":\"\"}";
ObjectMapper mapper = new ObjectMapper();
Teacher teacher = mapper.readValue(jsonString, Teacher.class);
System.out.println(mapper.writeValueAsString(teacher));
}
抛出异常:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `cn.javaee.util.jackson.config.Student` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('')
...
通过启用DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT配置,可以将空字符串""转化为null值的POJO对象。
/**
* 正确的将空字符串""转换为null值的Student对象
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void emptyStringToNullPOJO() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"student\":\"\"}";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
Teacher teacher = mapper.readValue(jsonString, Teacher.class);
System.out.println(mapper.writeValueAsString(teacher));
}
执行结果:
{"student":null}
另一个例子,Map的值为POJO,当POJO的值为空字符串时转化为null。
/**
* Map的值为POJO,当POJO的值为空字符串时转化为null
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void emptyStringToMapValue() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"sam\":\"\"}";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
Map<String, Student> map = mapper.readValue(jsonString, new TypeReference<Map<String, Student>>(){});
System.out.println(map);
}
执行结果:
{sam=null}
如果试图将空字符串""映射为null值的String字段,结果将出乎意料。
/**
* 并不支持将空字符串""转换为null值的String字段
*
* @throws JsonMappingException
* @throws JsonProcessingException
*/
@Test
public void convertFail() throws JsonMappingException, JsonProcessingException {
String jsonString = "{\"name\":\"\",\"age\":26}";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
Student student = mapper.readValue(jsonString, Student.class);
System.out.println(mapper.writeValueAsString(student));
}
执行结果:
{"name":"","age":26}
结果中的name仍为空字符串"",而不是null。这是该配置项比较难理解的地方,容易引起概念上的混淆。
按照官方github上cowtowncoder的回答,ACCEPT_EMPTY_STRING_AS_NULL_OBJECT命名ACCEPT_EMPTY_STRINGS_AS_NULL_POJO可能更好理解,因为该配置针对的是空字符串""与POJO对象之间的映射转换,并不支持String对象类型。
小结
Jackson提供了许多序列化和反序列化配置项,本篇讲了5种常用的配置:
- 序列化时格式化输出,增加可读性 ;
- 序列化空Bean时不抛出异常;
- 序列化日期禁用数字类型的时间戳输出,采用默认的字符串格式;
- 反序列化遇到不存在的字段时不抛出异常;
- 反序列化空字符串""映射为null值的POJO对象。
参考
https://github.com/FasterXML/jackson (opens in a new tab)
https://github.com/FasterXML/jackson-databind/issues/1563 (opens in a new tab)
https://github.com/FasterXML/jackson-dataformat-csv/issues/112 (opens in a new tab)