掘金 后端 ( ) • 2024-05-06 11:44

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

本系列 Jackson 版本 2.17.0

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

1. 前言

在上上篇文档中,有了解过JsonGenerator.FeatureJsonParser.Feature中的很多枚举项都已经被标记过时,官方对其进行了拆分:

  • JsonReadFeatureJsonWriteFeature(专门处理Json格式)
  • StreamReadFeatureStreamWriteFeature(处理任何格式)

2. JsonWriteFeature

一组特定于JSON格式的生成器特性,用于控制如何生成JSON文本,并确保输出的JSON是有效和符合规范的。

2.1 QUOTE_FIELD_NAMES

    QUOTE_FIELD_NAMES(true, Feature.QUOTE_FIELD_NAMES),

QUOTE_FIELD_NAMES配置属性名称是否使用双引号,默认开始。根据JSON规范,字段名应该使用双引号进行引用,这个特性允许开发者选择是否遵循这一规范。

关闭示例:

        UserInfo user = new UserInfo();
        user.setId(1767798780627279333L);
        user.setName("老三");
        user.setAge(25);
        
        JsonFactory jsonFactory = JsonFactory.builder()
                .configure(JsonWriteFeature.QUOTE_FIELD_NAMES, false)// 配置特性
                .build();
        JsonMapper jsonMapper=new JsonMapper(jsonFactory);
        String jsonStr = jsonMapper.writeValueAsString(user);
        System.out.println(jsonStr);

输出结果:

{id:1767798780627279333,name:"老三",userAge:25}

2.2 QUOTE_FIELD_NAMES

    WRITE_NAN_AS_STRINGS(true, Feature.QUOTE_NON_NUMERIC_NUMBERS),

如何处理JSON中的非数值(NaN)、正无穷大(Positive Infinity)和负无穷大("Negative Infinity)的浮点数和双精度数值。默认启用,非数值、正无穷大和负无穷大的浮点数或双精度数值会被作为JSON字符串输出,以遵守JSON规范。

该特征主要关注三种特殊的数值,同时,也会考虑对应的浮点数(Float)值:

  • Double.NaN(表示非数值)
  • Double.POSITIVE_INFINITY(表示正无穷大)
  • Double.NEGATIVE_INFINITY(表示负无穷大)

这设置一个Double.NaN

user.setaDouble(Double.NaN);

默认配置下,可以看到NaN有双引号,被当做字符串输出:

{"id":1767798780627279333,"aDouble":"NaN","name":"老三","age":25,"org":null,"roleList":null}

禁用该特征:

        JsonFactory jsonFactory = JsonFactory.builder()
                .configure(JsonWriteFeature.WRITE_NAN_AS_STRINGS, fasle)// 配置特性
                .build();

NaN没有双引号:

{"id":1767798780627279333,"aDouble":NaN,"name":"老三","age":25,"org":null,"roleList":null}

2.3 WRITE_NUMBERS_AS_STRINGS

    WRITE_NUMBERS_AS_STRINGS(false, Feature.WRITE_NUMBERS_AS_STRINGS),

WRITE_NUMBERS_AS_STRINGS强制将所有常规的数值作为JSON字符串输出,而不是作为JSON数字。默认禁用,Java的数值类型(intlongdouble)会使用它们的基本数值表示进行序列化。

例如开启后,idage被当做字符串进行了输出:

{"id":"1767798780627279333","aDouble":"NaN","name":"老三","age":"25","org":null,"roleList":null}

2.4 ESCAPE_NON_ASCII

    ESCAPE_NON_ASCII(false, Feature.ESCAPE_NON_ASCII),

ESCAPE_NON_ASCII配置如何处理7ASCII范围之外的字符(即Unicode码点在128及以上的字符)。

ASCII编码中,每个字符占用7位,因此其范围是从0127。超出这个范围的字符(即Unicode码点从128开始)在文本处理中通常需要特殊的编码方式,以便正确地传输和存储。

默认关闭,非ASCII字符在输出时不会使用转义,开启时,所有7ASCII范围之外的字符在输出时都需要使用特定于格式的转义序列。对于JSON格式,这通常意味着使用反斜杠(\)作为转义字符来表示这些特殊字符。

汉字是7ASCII范围之外的字符,默认输出如下:

{"name":"Hello!!!老三"}

当启用该特征后,输出如下:

{"name":"Hello!!!\u8001\u4E09"}

2.5 WRITE_HEX_UPPER_CASE

    WRITE_HEX_UPPER_CASE(true, Feature.WRITE_HEX_UPPER_CASE),

WRITE_HEX_UPPER_CASE用于配置十六进制值在序列化时是否使用大写字母。在JSON序列化过程中,当遇到需要转义的字节(比如不可打印的字符或控制字符)时,这些字节通常会被转换为十六进制格式,该特征默认开启,这些十六进制值会使用大写字母表示。

例如,字节值0x1F会被转换为\u001F而不是\u001f

2.6 ESCAPE_FORWARD_SLASHES

    ESCAPE_FORWARD_SLASHES(false, Feature.ESCAPE_FORWARD_SLASHES);

ESCAPE_FORWARD_SLASHES配置JsonGenerator是否应该转义正斜杠(/)。

例如网站是带正斜杠的,默认禁用,说明不会转义正斜杠,输出如下:

{"name":"https://www.baidu.com/","age":null,"org":null,"roleList":null}
{"name":"https:\/\/www.baidu.com\/","age":null,"org":null,"roleList":null}

3. JsonReadFeature

是一组特定于JSON格式的解析器特性,确保正确且高效地解析JSON文本,主要是提供了更多非标准JSON的处理适配,适用于某些非标准的情境或旧系统中,

3.1 ALLOW_JAVA_COMMENTS

    ALLOW_JAVA_COMMENTS(false, Feature.ALLOW_COMMENTS),

ALLOW_JAVA_COMMENTS配置JSON解析器是否允许在解析的内容中使用Java/C/C++风格的注释(包括/*//两种形式)。

默认开启,例如下方JSON文本包含了注释:

        String jsonWithComments = "{\"name\":\"John\", /* 多行注释 */ \"age\":30, // 单行注释\n\"addr\":\"长沙\"}";
        User userByComments = jsonMapper.readValue(jsonWithComments, User.class);
        System.out.println(userByComments);

直接解析会报错:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Unexpected character ('/' (code 47)): maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 17]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2643)

启用后,可正常解析:

User{id=null, aByte=0, aDouble=null, name='John', age=30, addr='长沙', org=null, roleList=null}

JSON规范并不支持注释,尽量避免在JSON数据中使用注释。

3.2 ALLOW_YAML_COMMENTS

ALLOW_YAML_COMMENTS用于配置JSON解析器是否应该允许在解析的内容中使用YAML风格的注释(即以#开头的行)。

    ALLOW_YAML_COMMENTS(false, JsonParser.Feature.ALLOW_YAML_COMMENTS),

和上面的类似,尽量避免使用。

3.3 ALLOW_SINGLE_QUOTES

    ALLOW_SINGLE_QUOTES(false, JsonParser.Feature.ALLOW_SINGLE_QUOTES),

ALLOW_UNQUOTED_FIELD_NAMES用于配置JSON解析器是否应该允许在解析的内容中使用单引号(')来替代双引号(")作为字符串的界定符。

开启后输出如下:

User{id=null, aByte=0, aDouble=null, name='John', age=30, addr='长沙', org=null, roleList=null}

JSON规范明确指定字符串应该由双引号包围,适用于某些非标准的情境或旧系统中,可能会遇到使用单引号的情况。

3.4 ALLOW_UNQUOTED_FIELD_NAMES

    ALLOW_UNQUOTED_FIELD_NAMES(false, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES),

ALLOW_UNQUOTED_FIELD_NAMES用于配置允许JSON解析器解析那些字段名没有用引号包围的JSON字符串。

下方JSON文本中,有的字段没有双引号:

        String jsonWithComments = "{name:'John', age:30, city:'长沙'}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Unexpected character ('i' (code 105)): was expecting double-quote to start field name
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2643)
	at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:685)
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddName(ReaderBasedJsonParser.java:1910)

开启后,则正常解析:

User{id=1767798780627279333, aByte=0, aDouble=null, name='null', age=30, addr='null', org=null, roleList=null}

3.5 ALLOW_UNESCAPED_CONTROL_CHARS

    ALLOW_UNESCAPED_CONTROL_CHARS(false, JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS),

ALLOW_UNESCAPED_CONTROL_CHARS用于配置允许JSON解析器解析包含未转义的控制字符的JSON字符串。

控制字符(如换行符\n、制表符\t等)在标准的JSON中通常需要转义,以确保数据的正确解析和跨平台的兼容性。然而,在某些情境下,可能会遇到包含未转义控制字符的JSON数据。

下方JSON文本中,包含没有转义的\n

        String jsonWithUnescapedControlChars = "{\"name\":\"Hello, World!\nThis is a new line.\"}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 23] (through reference chain: com.pearl.jacksoncore.demo.feature.User["name"])
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:402)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:361)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1937)

开启后,则正常解析:

User{id=null, aByte=0, aDouble=null, name='Hello, World!
This is a new line.', age=null, addr='null', org=null, roleList=null}

3.6 ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER

    ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false, JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER),

在标准的JSON规范中,反斜杠\用于转义一些特殊字符,如引号"、反斜杠\\、斜杠/、退格符\b、换行符\n、回车符\r和制表符\tALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER用于配置允许JSON解析器解析那些使用反斜杠\来转义任意字符的JSON字符串。

下方JSON文本中,name对应的值包含了使用\转义的字符,这在标准的JSON中是不允许的::

        String jsonWithNonStandardEscaping = "{\"name\":\"value1 with \\escaped\\ chars\", \"addr\":\"value2\"}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Unrecognized character escape 'e' (code 101)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 23] (through reference chain: com.pearl.jacksoncore.demo.feature.User["name"])
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:402)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:361)

开启后,则正常解析:

User{id=null, aByte=0, aDouble=null, name='value1 with escaped chars', age=null, addr='value2', org=null, roleList=null}

3.7 ALLOW_LEADING_ZEROS_FOR_NUMBERS

ALLOW_LEADING_ZEROS_FOR_NUMBERS用于配置允许JSON解析器解析包含前导零的数字。

    ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS),

下方JSON文本中,age对应的值包含了前导零,这在标准的JSON中是不允许的::

        String jsonWithLeadingZeros = "{\"age\": 0123, \"name\": \"张二码子\"}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Invalid numeric value: Leading zeroes not allowed
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 10]
	at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2567)
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2593)

开启后,则正常解析:

User{id=null, aByte=0, aDouble=null, name='张二码子', age=123, addr='null', org=null, roleList=null}

3.8 ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS

    ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS),

ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS用于配置允许JSON解析器解析包含前导加号+的数字。

标准的JSON规范中,数字不应该包含前导加号,因为加号通常用于表示正数,下方JSON文本中,age对应的值包含前导加号:

        String jsonWithLeadingPlusSign = "{\"age\": +123, \"name\": \"空空\"}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Unexpected character ('+' (code 43)) in numeric value: JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 10]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2643)

开启后,则正常解析:

User{id=null, aByte=0, aDouble=null, name='空空', age=123, addr='null', org=null, roleList=null}

3.9 ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS

    ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS),

ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS用于配置允许JSON解析器解析包含前导小数点的数字,标准的JSON格式中,数字不应该包含前导小数点,因为这样的表示方式通常用于表示浮点数,但前导小数点而没有整数部分是不合法的。

和上面的类似,可以自己试下:

  String jsonWithLeadingPlusSign = "{\"age\": .123, \"name\": \"空空\"}";

3.10 ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS

    ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS),

ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS用于配置允许JSON解析器解析包含尾随小数点的数字。

和上面的类似,可以自己试下:

      String jsonWithLeadingPlusSign = "{\"age\": 123., \"name\": \"王大\"}";

3.11 ALLOW_NON_NUMERIC_NUMBERS

    ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS),

ALLOW_NON_NUMERIC_NUMBERS用于配置允许解析器识别一组“非数字”(NaN)标记作为合法的浮点数值。JSON规范本身并不允许使用这些值,因此这是一个非标准的特性,默认是禁用的。

这个特性支持XML Schema中定义的一组特定的NaN值表示:

  • INF:表示正无穷大,也可以使用别名Infinity
  • -INF:表示负无穷大,别名可以是-Infinity
  • NaN:表示非数字,比如除以零的结果

3.12 ALLOW_MISSING_VALUES

    ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS),

ALLOW_MISSING_VALUES允许JSON解析器在遇到缺失值(即逗号后没有跟随任何值的情况)时不会抛出异常,而是会将其视为一个有效的输入。

下方JSON文本中,id没有对应的值:

        String jsonWithLeadingPlusSign = "{\"id\": ,\"name\": \"老八\"}";

默认关闭,直接解析会报异常:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Unexpected character (',' (code 44)): expected a valid value (JSON String, Number (or 'NaN'/'+INF'/'-INF'), Array, Object or token 'null', 'true' or 'false')
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 8]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2643)
	at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:685)

开启后,则正常解析:

User{id=null, aByte=0, aDouble=null, name='老八', age=null, addr='null', org=null, roleList=null}

3.13 ALLOW_TRAILING_COMMA

    ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA),

ALLOW_TRAILING_COMMA用于配置允许JSON解析器在处理JSON对象或数组时忽略尾随的逗号。

和上面的类似,可以自己试下:

        String jsonWithLeadingPlusSign = "{\"id\": ,\"name\": \"老八\",}";