ALLOW_MISSING_VALUES(false)
自2.10版本后,使用JsonReadFeature#ALLOW_MISSING_VALUES代替
是否允许支持JSON数组中“缺失”值。怎么理解:数组中缺失了值表示两个逗号之间,啥都没有,形如这样[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,有兴趣的亲可自行尝试,巩固下理解的效果。报错原因文上已有说明~
ALLOW_TRAILING_COMMA(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标准之外,给出了两个校验相关的特征。
STRICT_DUPLICATE_DETECTION(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]
GNORE_UNDEFINED(false)
自2.10版本后,使用StreamReadFeature#IGNORE_UNDEFINED代替
是否忽略没有定义的属性key。和JsonGenerator.Feature#IGNORE_UNKNOWN的这个特征一样,它作用于预先定义了格式的数据类型,如Avro、protobuf等等,JSON是不需要预先定义的哦~
同样的,你可以通过这个API预先设置格式:
JsonParser: public void setSchema(FormatSchema schema) { ... }
其它
INCLUDE_SOURCE_IN_LOCATION(true)
自2.10版本后,使用StreamReadFeature#INCLUDE_SOURCE_IN_LOCATION代替
是否构建JsonLocation对象来表示每个part的来源,你可以通过JsonParser#getCurrentLocation()来访问。作用不大,就此略过。
总结
本文介绍了底层流式API JsonParser读JSON的方式,它不仅仅能够处理标准JSON,也能通过Feature特征值来控制,开启对一些非标准但又比较常用的JSON串的支持,这不正式一个优秀框架/库应有的态度麽:兼容性。
结合上篇文章对写JSON时JsonGenerator的描述,能够总结出两点原则:
- 写:100%遵循规范
- 读:最大程度兼容并包
写代表你的输出,遵循规范的输出能确保第三方在用你输出的数据时不至于对你破口大骂,所以这是你应该做好的本分。读代表你的输入,能够处理规范的格式是你的职责,但我若还能额外的处理一些非标准格式(一般为常用的),那绝对是闪耀点,也就是你给的情分。本分是你应该做的,而情分就是你的加分项。