Jackson 2.x 系列【13】特征配置篇之 DeserializationFeature

735 阅读11分钟

有道无术,术尚可求,有术无道,止于术。

本系列 Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

1. 前言

接下来,我们分类介绍DeserializationFeature提供的所有特征枚举。DeserializationFeature用于定义了一组影响JSON文本反序列化方式的特性,这些特性既可以通过ObjectMapper设置(作为默认值),也可以通过ObjectReader设置。

2. 值处理

2.1 USE_BIG_DECIMAL_FOR_FLOATS

USE_BIG_DECIMAL_FOR_FLOATS(false),

USE_BIG_DECIMAL_FOR_FLOATS用于配置是否应将 JSON 中的浮点数反序列化为 BigDecimal 对象。默认禁用,因为 Double 类型通常能提供足够的精度,并且性能更好。

2.2 USE_BIG_INTEGER_FOR_INTS

USE_BIG_INTEGER_FOR_INTS(false),

USE_BIG_INTEGER_FOR_INTS用于配置是否应将 JSON 中的整数(非浮点数)反序列化为 BigInteger对象。默认禁用,未指定类型的整数默认将使用最紧凑的整数类型进行反序列化,以优化效率。

2.3 USE_LONG_FOR_INTS

USE_LONG_FOR_INTS(false),

USE_LONG_FOR_INTS用于配置是否应将 JSON 中的整数反序列化为long类型。默认禁用,将其反序列化为 int 类型(如果值在 int 范围内),或者如果值太大则可能使用 BigInteger。启用时整数值将被反序列化为 long 对象。

2.4 USE_JAVA_ARRAY_FOR_JSON_ARRAY

USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),

USE_JAVA_ARRAY_FOR_JSON_ARRAY用于配置在没有明确的类型信息时,是否应将 JSON 数组反序列化为原生 Java 数组(如 int[], String[] 等)而不是 List 或其他集合类型。

3. 错误处理

3.1 FAIL_ON_UNKNOWN_PROPERTIES

    FAIL_ON_UNKNOWN_PROPERTIES(true),

FAIL_ON_UNKNOWN_PROPERTIES用于配置在反序列化过程中遇到未知属性(即那些没有对应属性来映射的属性,且没有任何setterhandler可以处理这样的属性)时,应该如何处理。

默认启用,如果在JSON中存在无法映射到目标Java对象的属性,将抛出一个异常,指示反序列化失败。这样的设计可以帮助开发者及时捕获到数据模型与JSON结构之间的不匹配,从而进行修复。

例如下方代码中,User类没有pwd属性进行反序列化:

        String jsonStr="{\"id\":1767798780627279333,\"name\":\"张三\",\"pwd\":null,\"birthday\":1712043815828}";
        User user = jsonMapper.readValue(jsonStr, User.class);

默认情况下会报错:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "pwd" (class com.pearl.jacksoncore.demo.feature.User), not marked as ignorable (10 known properties: "addr", "orgList", "org", "num", "id", "birthday", "age", "aDouble", "roleList", "name"])
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 49] (through reference chain: com.pearl.jacksoncore.demo.feature.User["pwd"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1153)

禁用該特性,則正常輸出:

        String jsonStr="{\"id\":1767798780627279333,\"name\":\"张三\",\"pwd\":null,\"birthday\":1712043815828}";
        jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        User user = jsonMapper.readValue(jsonStr, User.class);

3.2 FAIL_ON_NULL_FOR_PRIMITIVES

    FAIL_ON_NULL_FOR_PRIMITIVES(false),

FAIL_ON_NULL_FOR_PRIMITIVES用于配置将 JSON 中的 null 值反序列化为 Java 基本类型(如 int, boolean, double 等)时是否抛出异常。

当前有一个用户类,有一个Long类型,一个long类型属性:

public class User {

    private Long id;

    private long userId;
	// ..............
}

执行序列化:

        User userBy = new User();
        userBy.setName("仓大大");
        String value = jsonMapper.writeValueAsString(userBy);
        System.out.println(value);

Long输出为nulllong输出为默认值0

{"id":null,"userId":0}

默认关闭,当long对应的值为null时,并不会抛出异常,开启后抛出MismatchedInputException::

        String str="{\"id\":null,\"userId\":null}";
        jsonMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,true);
        User userByStr = jsonMapper.readValue(str, User.class);
        System.out.println(userByStr);

3.3 FAIL_ON_NUMBERS_FOR_ENUMS

    FAIL_ON_NUMBERS_FOR_ENUMS(false),

FAIL_ON_NUMBERS_FOR_ENUMS用于配置JSON中的数字试图被反序列化为 Java 枚举类型时,是否抛出异常。

3.4 FAIL_ON_INVALID_SUBTYPE

    FAIL_ON_INVALID_SUBTYPE(true),

FAIL_ON_INVALID_SUBTYPE用于配置在反序列化过程中遇到无效的子类型时,是否抛出异常。

3.5 FAIL_ON_READING_DUP_TREE_KEY

    FAIL_ON_READING_DUP_TREE_KEY(false),

FAIL_ON_READING_DUP_TREE_KEY用于配置在解析 JSON 树(Tree Model)时遇到重复的键时,是否抛出异常。

3.6 FAIL_ON_IGNORED_PROPERTIES

    FAIL_ON_IGNORED_PROPERTIES(false),

例如下方代码中,User类中的id属性標記了忽略:

public class User {
    // 用户ID
    @JsonIgnore
    private Long id;
	//.........
}

默认配置下,反序列化时并不会报错:

        // FAIL_ON_IGNORED_PROPERTIES
        String strByTest = "{\"id\":123456,\"name\":null}";

        User userByTest = jsonMapper.readValue(strByTest, User.class);
        System.out.println(userByTest);

开启配置后,JSON内容存在被忽略的字段,则会报错:

        jsonMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true);
        
		Exception in thread "main" com.fasterxml.jackson.databind.exc.IgnoredPropertyException: Ignored field "id" (class com.pearl.jacksoncore.demo.feature.User) encountered; mapper configured not to allow this (10 known properties: "addr", "orgList", "org", "userId", "name", "birthday", "age", "aDouble", "roleList", "num"])
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 9] (through reference chain: com.pearl.jacksoncore.demo.feature.User["id"])
	at com.fasterxml.jackson.databind.exc.IgnoredPropertyException.from(IgnoredPropertyException.java:65)

3.7 FAIL_ON_UNRESOLVED_OBJECT_IDS

    FAIL_ON_UNRESOLVED_OBJECT_IDS(true),

FAIL_ON_UNRESOLVED_OBJECT_IDS用于配置在反序列化过程中遇到无法解析的对象ID时,是否抛出异常。

Jackson 中,可以通过对象 ID 来引用已经序列化过的对象,用来处理循环引用问题。对象 ID 通常是通过 @JsonIdentityInfo 注解来标记的,允许在序列化时给每个对象分配一个唯一的 ID,并在反序列化时通过这个 ID 来恢复对象引用。

如果在反序列化过程中 Jackson 遇到了一个对象 ID,但无法找到与之对应的已序列化对象(可能是因为对象没有被正确序列化,或者 ID 引用丢失),默认配置下,会抛出异常。

3.8 FAIL_ON_MISSING_CREATOR_PROPERTIES

    FAIL_ON_MISSING_CREATOR_PROPERTIES(false),

FAIL_ON_MISSING_CREATOR_PROPERTIES则用于在尝试通过创建者创建对象,发现缺少必需的属性时,是否抛出异常。

在反序列化时,对象的创建通常依赖于其构造函数、工厂方法或构建器模式,它们统称为“创建者”(creator)。创建者可以具有必需的属性(在构造函数中通过参数提供)或可选的属性(通过setter方法或构建器链式调用设置)。

3.9 FAIL_ON_NULL_CREATOR_PROPERTIES

    FAIL_ON_NULL_CREATOR_PROPERTIES(false),

FAIL_ON_NULL_CREATOR_PROPERTIES在反序列化过程中创建者属性值为 null时,是否抛出异常。

3.10 FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY

    FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true),

FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY用于配置在反序列化多态类型时,如果没有找到表示外部类型 ID 的属性时,是否抛出异常。

3.11 FAIL_ON_TRAILING_TOKENS

    FAIL_ON_TRAILING_TOKENS(false),

FAIL_ON_TRAILING_TOKENS用于配置在解析完一个完整的JSON对象后遇到额外的令牌时,是否抛出异常。

Jackson 解析 JSON 数据时,会按照 JSON 数据的结构顺序逐个读取并处理令牌。令牌可以包括对象的开始和结束标记、数组的开始和结束标记、属性名称、属性值等。在正常情况下,解析器会期望在读取完所有的有效令牌后遇到 JSON 数据的结束(比如文件末尾或输入流的结束)。

然而,在某些情况下,JSON 数据后面可能跟有额外的、不属于该 JSON 对象的令牌。 这可能是因为数据源在 JSON 数据之后包含了其他数据,或者是因为数据在传输过程中被意外地截断了。当遇到额外令牌时,禁用该特征会忽略尾随的令牌并继续执行。启用时,则会排除异常。

3.12 WRAP_EXCEPTIONS

    WRAP_EXCEPTIONS(true),

WRAP_EXCEPTIONS用于配置在遇到异常时是否应该捕获并包装这些异常,包装异常的目的是为了添加关于问题位置的额外信息(例如,在输入数据中的哪个位置出现了问题)。

3.13 FAIL_ON_UNEXPECTED_VIEW_PROPERTIES

    FAIL_ON_UNEXPECTED_VIEW_PROPERTIES(false),

FAIL_ON_UNEXPECTED_VIEW_PROPERTIES用于配置在反序列化过程中,遇到了不属于当前视图的属性时,是否抛出异常。

Jackson中的视图机制,允许定义哪些属性应该被序列化或反序列化到特定的上下文或用途。通常用于 API 版本控制、数据脱敏、不同客户端的定制数据。通过使用视图,可以控制哪些属性在特定的序列化或反序列化操作中是可用的。

4. 结构转换

4.1 ACCEPT_SINGLE_VALUE_AS_ARRAY

    ACCEPT_SINGLE_VALUE_AS_ARRAY(false),

ACCEPT_SINGLE_VALUE_AS_ARRAY用于配置是否允许单个值反序列化为数组。

正常情况下,List或者数组类型,序列化后,都是使用[]表示,也可以使用SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED配置单个元素时,不用[]表示。那么将单个反序列化为数组类型时,可以使用ACCEPT_SINGLE_VALUE_AS_ARRAY支持该场景。

4.2 UNWRAP_SINGLE_VALUE_ARRAYS

    UNWRAP_SINGLE_VALUE_ARRAYS(false),

UNWRAP_SINGLE_VALUE_ARRAYS用于处理 JSON 数组中只包含一个单独值的情况。当启用这个选项时,会在反序列化过程中自动“拆包”只包含一个元素的数组,将其直接映射到目标对象的属性上,而不是将该元素作为数组的一部分。

例如,当前JSON文本中,name字段是一个数组,但是在User类中,name字段是一个字符串,这时会报错:

        String strByUNWRAP_SINGLE_VALUE_ARRAYS = "{\"name\": [\"John\"]}";

开启该特性,单个元素的集合将会被反序列化为单个值:

        String strByUNWRAP_SINGLE_VALUE_ARRAYS = "{\"name\": [\"John\"]}";
        jsonMapper.configure(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, true);
        User userByByUNWRAP_SINGLE_VALUE_ARRAYS = jsonMapper.readValue(strByUNWRAP_SINGLE_VALUE_ARRAYS, User.class);
        System.out.println(userByByUNWRAP_SINGLE_VALUE_ARRAYS);

4.3 UNWRAP_ROOT_VALUE

UNWRAP_ROOT_VALUE(false),

SerializationFeature.WRAP_ROOT_VALUE在序列化时,最外会加一层根对象包装,开启UNWRAP_ROOT_VALUE,则可以将对象的根级别属性直接映射到目标Java对象的属性上,而不是将整个JSON对象作为一个嵌套的对象来处理。

5. 值转换

5.1 ACCEPT_EMPTY_STRING_AS_NULL_OBJECT

    ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),

ACCEPT_EMPTY_STRING_AS_NULL_OBJECT用于配置是否允许将空字符串值("")反序列化为null。 这里只支持属性类型为POJOMapCollection对象,如果是String类型则还是空字符串值。

image.png 例如,下方代碼中, name属性和org属性都是空字符串:

 String strByAAA ="{\"name\":\"\",\"age\":30,\"org\":\"\"}";

反序列化时会报错:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot coerce empty String ("") to `com.pearl.jacksoncore.demo.pojo.Org` value (but could if coercion was enabled using `CoercionConfig`)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 27] (through reference chain: com.pearl.jacksoncore.demo.feature.User["org"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadCoercion(DeserializationContext.java:1832)

开启该特征则正常输出:

        String strByAAA ="{\"name\":\"\",\"age\":30,\"org\":\"\"}";
        jsonMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        User userByAAA  = jsonMapper.readValue(strByAAA, User.class);

5.2 ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT

    ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false),

ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT用于配置是否允许将空数组([])反序列化为 null ,和上面的类似。

5.3 ACCEPT_FLOAT_AS_INT

    ACCEPT_FLOAT_AS_INT(true),

ACCEPT_FLOAT_AS_INT用于配置是否允许将浮点类型反序列化为 int类型 。

示例代码:

        String strByAAAA ="{\"name\":\"\",\"age\":30,\"age\":3.14159}";
        User userByAAAA  = jsonMapper.readValue(strByAAAA, User.class);
        System.out.println(userByAAAA);

输出结果:

User{id=null, aDouble=null, name='', age=3, addr='null', org=null, roleList=null}

5.4 READ_ENUMS_USING_TO_STRING

    READ_ENUMS_USING_TO_STRING(false),

READ_ENUMS_USING_TO_STRING用于配置允许在反序列化枚举值的过程中,尝试将字符串与枚举类型中的 toString()方法法返回的所有值进行匹配,以找到正确的枚举实例。

提供一个示例性别枚举:

public enum GenderEnum {

    MAN("man", "男"),

    WOMAN("woman", "女");

    GenderEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    private String code;

    private String desc;

    @Override
    public String toString() {
        return desc; 
    }
    // ............
 }   

进行序列化,从输出结果可以看出,默认输出的是Enum.name() 方法的返回值:

        GenderEnum genderEnum = GenderEnum.MAN;
        String strByGenderEnum = jsonMapper.writeValueAsString(genderEnum);
        System.out.println(GenderEnum.MAN.name()); //  MAN
        System.out.println(strByGenderEnum); // "MAN"

在上一篇,有学过可以使用SerializationFeature.WRITE_ENUMS_USING_TO_STRING调用 toString()的返回值进行输出:

jsonMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true); //  "男"

反序列化时,可以开启READ_ENUMS_USING_TO_STRING,调用 toString()方法进行匹配:

        jsonMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
        String strEnum="\"男\"";
        GenderEnum genderEnumByStr = jsonMapper.readValue(strEnum, GenderEnum.class);
        System.out.println(genderEnumByStr);

5.5 READ_UNKNOWN_ENUM_VALUES_AS_NULL

    READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),

READ_UNKNOWN_ENUM_VALUES_AS_NULL用于配置在反序列化枚举类型时,如果遇到未知的枚举值,是否作为null处理。

例如反序列化一个不存在的枚举值:

        String strEnumAAA="\"人妖\"";
        //jsonMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
        GenderEnum strEnumAAAByStr = jsonMapper.readValue(strEnumAAA, GenderEnum.class);
        System.out.println(strEnumAAAByStr);

默认配置下,会抛出异常:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.pearl.jacksoncore.demo.feature.GenderEnum` from String "人妖": not one of the values accepted for Enum class: [女, 男]
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)

开启该特征后,反序列化为null

null

5.6 READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE

    READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),

示例,首先使用@JsonEnumDefaultValue标识一个默认值:

public enum GenderEnum {

    MAN("man", "男"),

    WOMAN("woman", "女"),
    @JsonEnumDefaultValue
    UNKNOWN("unknown", "默认值")
    ;
    // ............
 }  

开启特性并反序列化一个未知枚举值:

        // READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE
        String strEnumAAA="\"人妖\"";
        jsonMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
        GenderEnum strEnumAAAByStr = jsonMapper.readValue(strEnumAAA, GenderEnum.class);
        System.out.println(strEnumAAAByStr); // 默认值

5.7 READ_DATE_TIMESTAMPS_AS_NANOSECONDS

    READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true),

READ_DATE_TIMESTAMPS_AS_NANOSECONDS用于配置将日期和时间戳(Timestamps)反序列化为纳秒(Nanoseconds)精度的值。纳秒是时间的最小单位之一,比秒小得多。使用纳秒精度可以捕获更精细的时间变化,这在需要高精度时间记录的场合(如金融交易、科学计算等)非常有用。

5.8 ADJUST_DATES_TO_CONTEXT_TIME_ZONE

ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true),

ADJUST_DATES_TO_CONTEXT_TIME_ZONE用于配置将日期值反序列化为和当前上下文相关的时区。

6. 其他

6.1 EAGER_DESERIALIZER_FETCH

EAGER_DESERIALIZER_FETCH(true);

EAGER_DESERIALIZER_FETCH用于配置ObjectReader是否应该在可能的情况下积极地获取必要的JsonDeserializer。默认是启用的,除非确实遇到了实际问题,否則没有必要禁用这个特性。