Jackson常用的序列化和反序列化配置项

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注解设置日期格式一讲中提到,为字段添加@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对象。

参考

《轻松学习Jackson》

https://github.com/FasterXML/jackson

https://github.com/FasterXML/jackson-databind/issues/1563

https://github.com/FasterXML/jackson-dataformat-csv/issues/112

https://stackoverflow.com/questions/22688713/jackson-objectmapper-deserializationconfig-feature-accept-empty-string-as-null-o


---转载本站文章请注明作者和出处 996极客教程(996geek.com),请勿用于任何商业用途---

留下评论