版本约定
Jackson 版本:2.11.0Spring Framework 版本:5.2.6.RELEASESpring Boot 版本:2.3.0.RELEASE
什么叫读 JSON?就是把一个 JSON字符串解析为对象 or 树模型嘛,因此也称作解析 JSON 串。Jackson 底层流式 API 使用JsonParser来完成JSON 字符串的解析。
最简使用 Demo
准备一个 POJO:
测试用例:把一个 JSON 字符串绑定(封装)进一个 POJO 对象里
运行程序,输出:
成功把一个 JSON 字符串的值解析到 Person 对象。你可能会疑问,怎么这么麻烦?那当然,这是底层流式 API,纯手动档嘛。你获得了性能,可不要失去一些便捷性嘛。
JsonParser针对不同的 value 类型,提供了非常多的方法用于实际值的获取。
直接值获取:
这类方法可能会抛出异常:比如 value 值本不是数字但你调用了 getInValue()方法~
带默认值的值获取,具有更好安全性:
此类方法若碰到数据的转换失败时,不会抛出异常,把def作为默认值返回。
组合方法
同JsonGenerator一样,JsonParser 也提供了高钙片组合方法,让你更加便捷的使用。
自动绑定
听起来像高级功能,是的,它必须依赖于ObjectCodec去实现,因为实际是全部委托给了它去完成的,也就是我们最为熟悉的 readXXX 系列方法:
我们知道,ObjectMapper 就是一个 ObjectCodec,它属于高级 API,本文显然不会用到 ObjectMapper 它喽,因此我们自己手敲一个实现来完成此功能。
自定义一个 ObjectCodec,Person 类专用:用于把 JSON 串自动绑定到实例属性。
有了它,就可以实现我们的自动绑定了,书写测试用例:
运行程序,输出:
这就是 ObjectMapper 自动绑定的核心原理所在,其它更为强大能力将在后续章节详细展开。
JsonToken
在上例解析过程中,有一个非常重要的角色,那便是:JsonToken。它表示解析 JSON 内容时,用于返回结果的基本标记类型的枚举。
为了辅助理解,A 哥用一个例子,输出各个部分一目了然:
运行程序,输出:
从左至右解析,一一对应。各个部分用下面这张图可以简略表示出来:
JsonParser 的 Feature
它是 JsonParser 的一个内部枚举类,共 15 个枚举值:
每个枚举值都控制着JsonParser不同的行为。下面分类进行解释
底层 I/O 流相关
Jackson 的流式 API 指的是 I/O 流,所以即使是读,底层也是用 I/O 流(Reader)去读取然后解析的。
AUTOCLOSESOURCE(true)
原理和 JsonGenerator 的AUTO_CLOSE_TARGET(true)一样,不再解释。
支持非标准格式
JSON 是有规范的,在它的规范里并没有描述到对注释的规定、对控制字符的处理等等,也就是说这些均属于非标准行为。比如这个 JSON 串:
你看,若你这么写 IDEA 都会飘红提示你:
但是,在很多使用场景(特别是 JavaScript)里,我们会在 JSON 串里写注释(属性多时尤甚)那么对于这种串,JsonParser 如何控制处理呢?它提供了对非标准 JSON 格式的兼容,通过下面这些特征值来控制。
ALLOW_COMMENTS(false)
是否允许/* */或者//这种类型的注释出现。
运行程序,抛出异常:
放开注释的代码,再次运行程序,正常 work。
ALLOWYAMLCOMMENTS(false)
顾名思义,开启后将支持 Yaml 格式的的注释,也就是#形式的注释语法。
ALLOWUNQUOTEDFIELD_NAMES(false)
是否允许属性名不带双引号””,比较简单,示例略。
ALLOWSINGLEQUOTES(false)
是否允许属性名支持单引号,也就是使用”包裹,形如这样:
ALLOWUNQUOTEDCONTROL_CHARS(false)
是否允许 JSON 字符串包含非引号控制字符(值小于 32 的 ASCII 字符,包含制表符和换行符)。 由于 JSON 规范要求对所有控制字符使用引号,这是一个非标准的特性,因此默认禁用。
那么,哪些字符属于控制字符呢?做个简单科普:我们一般说的 ASCII 码共 128 个字符(7bit),共分为两大类
控制字符
控制字符,也叫不可打印字符。第0~32 号及第 127 号(共 34 个)是控制字符,例如常见的:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)等都属于此类。
控制字符大部分已经废弃不用了,它们的用途主要是用来操控已经处理过的文字,ASCII 值为 8、9、10 和 13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。
非控制字符
也叫可显示字符,或者可打印字符,能从键盘直接输入的字符。比如 0-9 数字,逗号、分号这些等等。
ALLOWBACKSLASHESCAPINGANYCHARACTER(false)
是否允许反斜杠转义任何字符。这句话不是非常好理解,看下面这个例子:
运行程序,报错:
放开注释掉的代码,再次运行程序,一切正常,输出:YourB’at***n。
ALLOWNUMERICLEADING_ZEROS(false)
是否允许像00001这样的“数字”出现(而不报错)。看例子:
运行程序,输出:
放开注掉的代码,再次运行程序,一切正常。输出18。
ALLOWLEADINGDECIMALPOINTFOR_NUMBERS(false)
是否允许小数点.打头,也就是说.1这种小数格式是否合法。默认是不合法的,需要开启此特征才能支持,例子就略了,基本同上。
ALLOWNONNUMERIC_NUMBERS(false)
是否允许一些解析器识别一组“非数字”(如 NaN)作为合法的浮点数值。这个属性和上篇文章的JsonGenerator#QUOTE_NON_NUMERIC_NUMBERS特征值是遥相呼应的。
运行程序,抛错:
放开注释掉的代码,再次运行,一切正常。输出:
ALLOWMISSINGVALUES(false)
是否允许支持JSON 数组中“缺失”值。怎么理解:数组中缺失了值表示两个逗号之间,啥都没有,形如这样[value1, , value3]。
运行程序,抛错:
放开注释掉的代码,再次运行,一切正常,结果为:
请注意:此时数组的长度是 5 哦。
ALLOWTRAILINGCOMMA(false)
是否允许最后一个多余的逗号(一定是最后一个)。这个特征是非常重要的,若开关打开,有如下效果:
[true,true,]等价于[true, true]{“a”: true,}等价于{“a”: true}
当这个特征和上面的ALLOW_MISSING_VALUES特征同时使用时,本特征优先级更高。也就是说:会先去除掉最后一个逗号后,再进行数组长度的计算。
举个例子:当然这两个特征开关都打开时,[true,true,]等价于[true, true]好理解;并且呢,[true,true,,]是等价于[true, true, null]的哦,可千万别忽略最后的这个 null。
运行程序,输出:
这完全就是上例的效果嘛。现在我放开注释掉的代码,再次运行,结果为:
请注意对比前后的结果差异,并自己能能自己合理解释。
校验相关
Jackson 在 JSON 标准之外,给出了两个校验相关的特征。
STRICTDUPLICATEDETECTION(false)
是否允许 JSON 串有两个相同的属性 key,默认是允许的。
运行程序,正常输出:
若放开注释代码,再次运行,则抛错:
IGNORE_UNDEFINED(false)
是否忽略没有定义的属性 key。和JsonGenerator.Feature#IGNORE_UNKNOWN的这个特征一样,它作用于预先定义了格式的数据类型,如Avro、protobuf等等,JSON 是不需要预先定义的哦~
同样的,你可以通过这个 API 预先设置格式:
其它
INCLUDESOURCEIN_LOCATION(true)
是否构建JsonLocation对象来表示每个 part 的来源,你可以通过JsonParser#getCurrentLocation()来访问。作用不大,就此略过。
总结
本文介绍了底层流式 API JsonParser 读 JSON 的方式,它不仅仅能够处理标准 JSON,也能通过 Feature 特征值来控制,开启对一些非标准但又比较常用的 JSON 串的支持,这不正式一个优秀框架/库应有的态度麽:兼容性。
结合上篇文章对写 JSON 时JsonGenerator的描述,能够总结出两点原则:
写:100%遵循规范读:最大程度
兼容
并包
写代表你的输出,遵循规范的输出能确保第三方在用你输出的数据时不至于对你破口大骂,所以这是你应该做好的本分。读代表你的输入,能够处理规范的格式是你的职责,但我若还能额外的处理一些非标准格式(一般为常用的),那绝对是闪耀点,也就是你给的情分。本分是你应该做的,而情分就是你的加分项
本文来自微笑向暖投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/564613.html