版本承诺
- Jackson 版本:2.11.0
- Spring Framework 版本:5.2.6.RELEASE
- Spring Boot 版本:2.3.0.RELEASE
什么是读 JSON?便是把一个 JSON 字符串 解析为目标 or 树实体模型嘛,因而也称之为解析 JSON 串。Jackson 底层流式 API 应用JsonParser来进行JSON 字符串的解析。
最简应用 Demo
提前准备一个 POJO:
@Data
public class Person {
private String name;
private Integer age;
}
功能测试:把一个 JSON 字符串关联(封装形式)进一个 POJO 目标里
@Test
public void test1() throws IOException {
String jsonStr = \"{\"name\":\"YourBatman\",\"age\":18}\";
Person person = new Person();
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// 只需还没完毕\"}\",就一直读
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"name\".equals(fieldname)) {
jsonParser.nextToken();
person.setName(jsonParser.getText());
} else if (\"age\".equals(fieldname)) {
jsonParser.nextToken();
person.setAge(jsonParser.getIntValue());
}
}
System.out.println(person);
}
}
运作程序流程,导出:
Person(name=YourBatman, age=18)
取得成功把一个 JSON 字符串的值解析到 Person 目标。你也许会疑惑,如何那么不便?那自然,这也是底层流式 API,纯手动档嘛。你得到了特性,可不必丧失一些便利性嘛。
小提示:底层流式 API 一般面对“专业人员”,运用级开发设计应用高级 API ObjectMapper就可以。自然,看完本系列产品就能使你彻底具有“专业人员”的整体实力
JsonParser对于不一样的 value 种类,给予了十分多的方式用以具体值的获得。
立即值获得:
// 获取字符串种类
public abstract String getText() throws IOException;
// 数据 Number 种类值 标量值(适用的 Number 种类参考 NumberType 枚举类型)
public abstract Number getNumberValue() throws IOException;
public enum NumberType {
INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL
};
public abstract int getIntValue() throws IOException;
public abstract long getLongValue() throws IOException;
...
public abstract byte[] getBinaryValue(Base64Variant bv) throws IOException;
这类方法也许会抛出异常:例如 value 值本并不是数据但你启用了 getInValue()方式~
小提示:假如 value 值是 null,像 getIntValue()、getBooleanValue()等这类立即获得方式是会抛出异常的,但 getText()不容易
带初始值的值获得,具备更强安全系数:
public String getValueAsString() throws IOException {
return getValueAsString(null);
}
public abstract String getValueAsString(String def) throws IOException;
...
public long getValueAsLong() throws IOException {
return getValueAsLong(0);
}
public abstract long getValueAsLong(long def) throws IOException;
...
该类方式若遇到数据信息的变换不成功时,不容易抛出异常,把def做为初始值回到。
组成方式
同JsonGenerator一样,JsonParser 也给予了高钙片组成方式,使你更为方便快捷的应用。
全自动关联
听起来像高端作用,是的,它务必取决于ObjectCodec去完成,由于具体是所有授权委托给了它去进行的,也就是大家最了解的 readXXX 系列产品方式:
我们知道,ObjectMapper 便是一个 ObjectCodec,它属于高端 API,文中显而易见不容易使用 ObjectMapper 它喽,因而我们自己手敲一个完成来实现此作用。
自定一个 ObjectCodec,Person 类专用型:用以把 JSON 串全自动关联到案例特性。
public class PersonObjectCodec extends ObjectCodec {
...
@SneakyThrows
@Override
public <T> T readValue(JsonParser jsonParser, Class<T> valueType) throws IOException {
Person person = (Person) valueType.newInstance();
// 只需还没完毕\"}\",就一直读
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"name\".equals(fieldname)) {
jsonParser.nextToken();
person.setName(jsonParser.getText());
} else if (\"age\".equals(fieldname)) {
jsonParser.nextToken();
person.setAge(jsonParser.getIntValue());
}
}
return (T) person;
}
...
}
拥有它,就可以完成咱们的全自动关联了,撰写功能测试:
@Test
public void test3() throws IOException {
String jsonStr = \"{\"name\":\"YourBatman\",\"age\":18, \"pickName\":null}\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
jsonParser.setCodec(new PersonObjectCodec());
System.out.println(jsonParser.readValueAs(Person.class));
}
}
运作程序流程,导出:
Person(name=YourBatman, age=18)
这就是 ObjectMapper 全自动关联的关键基本原理所属,其他更加强劲工作能力将在后面章节目录详尽进行。
JsonToken
在上例解析全过程中,有一个特别关键的人物角色,那就是:JsonToken。它表明解析 JSON 具体内容时,用于返回结论的基本上标记类型的枚举。
public enum JsonToken {
NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),
START_OBJECT(\"{\", JsonTokenId.ID_START_OBJECT),
END_OBJECT(\"}\", JsonTokenId.ID_END_OBJECT),
START_ARRAY(\"[\", JsonTokenId.ID_START_ARRAY),
END_ARRAY(\"]\", JsonTokenId.ID_END_ARRAY),
// 特性名(key)
FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),
// 值(value)
VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),
VALUE_STRING(null, JsonTokenId.ID_STRING),
VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),
VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),
VALUE_TRUE(\"true\", JsonTokenId.ID_TRUE),
VALUE_FALSE(\"false\", JsonTokenId.ID_FALSE),
VALUE_NULL(\"null\", JsonTokenId.ID_NULL),
}
为了更好地协助了解,A 哥用一个事例,导出每个一部分一目了然:
@Test
public void test2() throws IOException {
String jsonStr = \"{\"name\":\"YourBatman\",\"age\":18, \"pickName\":null}\";
System.out.println(jsonStr);
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
while (true) {
JsonToken token = jsonParser.nextToken();
System.out.println(token \" -> 数值:\" jsonParser.getValueAsString());
if (token == JsonToken.END_OBJECT) {
break;
}
}
}
}
运作程序流程,导出:
{\"name\":\"YourBatman\",\"age\":18, \"pickName\":null}
START_OBJECT -> 数值:null
FIELD_NAME -> 值为:name
VALUE_STRING -> 数值:YourBatman
FIELD_NAME -> 值为:age
VALUE_NUMBER_INT -> 数值:18
FIELD_NAME -> 值为:pickName
VALUE_NULL -> 数值:null
END_OBJECT -> 值为:null
从左至右解析,一一对应。每个一部分用下边这幅图可以简单表明出去:
小提示:解析时请保证你的的 JSON 串是正规的,不然抛出去JsonParseException出现异常
JsonParser 的 Feature
它是 JsonParser 的一个内部结构枚举类,共 15 个枚举值:
public enum Feature {
AUTO_CLOSE_SOURCE(true),
ALLOW_COMMENTS(false),
ALLOW_YAML_COMMENTS(false),
ALLOW_UNQUOTED_FIELD_NAMES(false),
ALLOW_SINGLE_QUOTES(false),
@Deprecated
ALLOW_UNQUOTED_CONTROL_CHARS(false),
@Deprecated
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),
@Deprecated
ALLOW_NUMERIC_LEADING_ZEROS(false),
@Deprecated
ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS(false),
@Deprecated
ALLOW_NON_NUMERIC_NUMBERS(false),
@Deprecated
ALLOW_MISSING_VALUES(false),
@Deprecated
ALLOW_TRAILING_COMMA(false),
STRICT_DUPLICATE_DETECTION(false),
IGNORE_UNDEFINED(false),
INCLUDE_SOURCE_IN_LOCATION(true);
}
小提示:枚举值均为 bool 类型,引号内为初始值
每一个枚举值都操纵着JsonParser不一样的个人行为。下边归类开展表述
最底层 I/O 流有关
自 2.10 版本号后,应用StreamReadFeature#AUTO_CLOSE_SOURCE替代
Jackson 的流式的 API 指的是 I/O 流,因此即使是读,最底层也是用 I/O 流(Reader)去载入随后解析的。
AUTOCLOSESOURCE(true)
基本原理和 JsonGenerator 的AUTO_CLOSE_TARGET(true)一样,不会再表述。
适用非标文件格式
JSON 是有规范化的,在它的标准里并并没有叙述到对注解的要求、对控制字符的处置这些,换句话说这种均属于非标个人行为。例如这一 JSON 串:
{
\"name\" : \"YourBarman\", // 名称
\"age\" : 18 // 年纪
}
你看看,若你那么写 IDEA 都是会标红提醒你:
可是,在许多应用情景(尤其是 JavaScript)里,大家会在 JSON 串里写注解(特性多时尤甚)那麼针对这类串,JsonParser 怎样操纵解决呢?它保证了对非标 JSON 文件格式的兼容,根据下边这种特征根来操纵。
ALLOW_COMMENTS(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_JAVA_COMMENTS替代
是不是容许/* */或是//这类类型的注解发生。
@Test
public void test4() throws IOException {
String jsonStr = \"{n\"
\"t\"name\" : \"YourBarman\", // 名称n\"
\"t\"age\" : 18 // 年纪n\"
\"}\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// 打开注解适用
// jsonParser.enable(JsonParser.Feature.ALLOW_COMMENTS);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"name\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getText());
} else if (\"age\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getIntValue());
}
}
}
}
运作程序流程,抛出异常:
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: (String)\"{
\"name\" : \"YourBarman\", // 名称
\"age\" : 18 // 年纪
}\"; line: 2, column: 26]
放宽注解的编码,再度运作程序流程,正常的 work。
ALLOWYAMLCOMMENTS(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_YAML_COMMENTS替代
说白了,打开后将适用 Yaml 文件格式的的注解,也就是#方式的注解英语的语法。
ALLOWUNQUOTEDFIELD_NAMES(false)
自 2.10 版本号后,应用JsonReadFeature#
ALLOW_UNQUOTED_FIELD_NAMES替代
是不是容许特性名没有双引号””,非常简单,实例略。
ALLOWSINGLEQUOTES(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_SINGLE_QUOTES替代
是不是容许特性名适用反斜杠,也就是应用”包囊,形如那样:
{
\'age\' : 18
}
ALLOWUNQUOTEDCONTROL_CHARS(false)
自 2.10 版本号后,应用JsonReadFeature#
ALLOW_UNESCAPED_CONTROL_CHARS替代
是不是容许 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)
自 2.10 版本号后,应用JsonReadFeature#
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER替代
是不是容许反斜杠转译一切标识符。这句话并不是很好了解,看下面这一事例:
@Test
public void test4() throws IOException {
String jsonStr = \"{\"name\" : \"YourB\\\'atman\" }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// jsonParser.enable(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"name\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getText());
}
}
}
}
运作程序流程,出错:
com.fasterxml.jackson.core.JsonParseException: Unrecognized character escape \'\'\' (code 39)
at [Source: (String)\"{\"name\" : \"YourB\'atman\" }\"; line: 1, column: 19]
...
放宽注解掉的编码,再度运作程序流程,一切正常,导出:YourB’atman。
ALLOWNUMERICLEADING_ZEROS(false)
自 2.10 版本号后,应用JsonReadFeature#
ALLOW_LEADING_ZEROS_FOR_NUMBERS替代
是不是容许像00001那样的“数据”发生(而不出错)。看事例:
@Test
public void test5() throws IOException {
String jsonStr = \"{\"age\" : 00018 }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// jsonParser.enable(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"age\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getIntValue());
}
}
}
}
运作程序流程,导出:
com.fasterxml.jackson.core.JsonParseException: Invalid numeric value: Leading zeroes not allowed
at [Source: (String)\"{\"age\" : 00018 }\"; line: 1, column: 11]
...
放宽注掉的编码,再度运作程序流程,一切正常。导出18。
ALLOWLEADINGDECIMALPOINTFOR_NUMBERS(false)
自 2.10 版本号后,应用JsonReadFeature#
ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS替代
是不是容许小数位.开头,换句话说.1这类小数文件格式是不是合理合法。默认设置不是正规的,必须打开此特点才可以适用,事例就略了,基本上跟上面一样。
ALLOWNONNUMERIC_NUMBERS(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS替代
是不是容许一些在线解析鉴别一组“非数据”(如 NaN)做为合理合法的浮点数标值。这一特性和上一篇文章的JsonGenerator#QUOTE_NON_NUMERIC_NUMBERS特征根是息息相通的。
@Test
public void test5() throws IOException {
String jsonStr = \"{\"percent\" : NaN }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// jsonParser.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"percent\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getFloatValue());
}
}
}
}
运行程序,抛错:
com.fasterxml.jackson.core.JsonParseException: Non-standard token \'NaN\': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow
at [Source: (String)\"{\"percent\" : NaN }\"; line: 1, column: 17]
放宽注解掉的编码,再度运行,一切正常。导出:
NaN
小提示:NaN 还可以代表一个 Float 目标,是的你没看错,即使它并不是数据但它也是 Float 种类。实际你能看一下 Float 源代码里的那好多个变量定义
ALLOWMISSINGVALUES(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_MISSING_VALUES替代
是不是容许适用JSON 二维数组中“缺少”值。怎么理解:二维数组中缺少了值表明2个分号中间,啥也没有,形如那样[value1, , value3]。
@Test
public void test6() throws IOException {
String jsonStr = \"{\"names\" : [\"YourBatman\",,\"A 哥\",,] }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// jsonParser.enable(JsonParser.Feature.ALLOW_MISSING_VALUES);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"names\".equals(fieldname)) {
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
System.out.println(jsonParser.getText());
}
}
}
}
}
运行程序,抛错:
YourBatman // 能导出一个,终究第一个 part(JsonToken)是常规的嘛
com.fasterxml.jackson.core.JsonParseException: Unexpected character (\',\' (code 44)): expected a valid value (JSON String, Number, Array, Object or token \'null\', \'true\' or \'false\')
at [Source: (String)\"{\"names\" : [\"YourBatman\",,\"A 哥\",,] }\"; line: 1, column: 27]
放宽注解掉的编码,再度运行,一切正常,结论为:
YourBatman
null
A 哥
null
null
小心:这时二维数组的长短是 5 哦。
小提示:这里用的 String 种类展现结论,是由于 null 可以做为 String 种类(jsonParser.getText()获得 null 是正规的)。但假如你应用的 int 种类(或是 bool 种类),那麼如果是 null 得话就出错喽Current token (VALUE_NULL) not of boolean type,有感兴趣的亲可自主试着,推进下了解的实际效果。出错缘故文上已经有表明~
ALLOWTRAILINGCOMMA(false)
自 2.10 版本号后,应用JsonReadFeature#ALLOW_TRAILING_COMMA替代
是不是容许最后一个不必要的分号(一定是最后一个)。这一特点是十分关键的,若电源开关开启,有如下所示实际效果:
- [true,true,]等额的于[true, true]
- {“a”: true,}等价于{“a”: true}
当这一特点和里面的ALLOW_MISSING_VALUES特点与此同时应用时,本特点优先更高一些。换句话说:会先去祛除最后一个分号后,再开展数组长度的测算。
举例说明:自然这两个特点电源开关都开启时,[true,true,]等额的于[true, true]好了解;而且呢,[true,true,,]是等额的于[true, true, null]的哦,可千万不要忽视最终的这一 null。
@Test
public void test7() throws IOException {
String jsonStr = \"{\"results\" : [true,true,,] }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
jsonParser.enable(JsonParser.Feature.ALLOW_MISSING_VALUES);
// jsonParser.enable(JsonParser.Feature.ALLOW_TRAILING_COMMA);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"results\".equals(fieldname)) {
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
System.out.println(jsonParser.getBooleanValue());
}
}
}
}
}
运行程序,导出:
YourBatman
null
A 哥
null
null
这完完全全便是上例的实际效果嘛。如今我放宽注解掉的编码,再度运行,结论为:
YourBatman
null
A 哥
null
小心比照前后左右的效果差别,并自身可以自身有效表述。
校检有关
Jackson 在 JSON 规范以外,得出了2个校检有关的特点。
STRICTDUPLICATEDETECTION(false)
自 2.10 版本号后,应用StreamReadFeature#
STRICT_DUPLICATE_DETECTION替代
是不是容许 JSON 串有两个一样的特性 key,默认设置是容许的。
@Test
public void test8() throws IOException {
String jsonStr = \"{\"age\":18, \"age\": 28 }\";
JsonFactory factory = new JsonFactory();
try (JsonParser jsonParser = factory.createParser(jsonStr)) {
// jsonParser.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if (\"age\".equals(fieldname)) {
jsonParser.nextToken();
System.out.println(jsonParser.getIntValue());
}
}
}
}
运行程序,正常的输出:
18
28
若放开注释编码,再度运行,则抛错:
18 // 第一个数据或是能正常的输出的哟
com.fasterxml.jackson.core.JsonParseException: Duplicate field \'age\'
at [Source: (String)\"{\"age\":18, \"age\": 28 }\"; line: 1, column: 17]
IGNORE_UNDEFINED(false)
自 2.10 版本号后,应用StreamReadFeature#IGNORE_UNDEFINED替代
是不是忽视并没有界定的特性 key。和JsonGenerator.Feature#IGNORE_UNKNOWN的这些特点一样,它效果于事先界定了文件格式的基本数据类型,如Avro、protobuf这些,JSON 是不用事先界定的哦~
一样的,你能利用这一 API 事先设定文件格式:
JsonParser:
public void setSchema(FormatSchema schema) {
...
}
其他
INCLUDESOURCEIN_LOCATION(true)
自 2.10 版本号后,应用StreamReadFeature#
INCLUDE_SOURCE_IN_LOCATION替代
是不是搭建JsonLocation目标来表明每一个 part 的由来,你能根据JsonParser#getCurrentLocation()来浏览。功效并不大,从此忽略。
汇总
文中详细介绍了最底层流式的 API JsonParser 读 JSON 的方法,它不仅可以解决规范 JSON,也可以根据 Feature 特征根来操纵,打开对一些非标但又较为常见的 JSON 串的适用,这并不宣布一个出色架构/库应该有的心态麽:兼容模式。
融合上一篇文章对写 JSON 时JsonGenerator的叙述,可以归纳出二点标准:
- 写:100%遵循规范
- 读:较大水平兼容并包
写意味着你的输出,遵循标准的输出能保证第三方在使用你输出的数据资料时不会对你痛骂,因此这是你应当搞好的本份。读意味着你的键入,可以解决标准的文件格式就是你的岗位职责,但我若还能附加的解决一些非标文件格式(一般为常见的),那絕對是绚丽点,也便是你给的情份。本份就是你需要做的,而情份便是你的加分项目
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。