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)去读取然后解析的。
AUTO_CLOSE_SOURCE(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。
ALLOW_YAML_COMMENTS(false)
自2.10版本后,使用JsonReadFeature#ALLOW_YAML_COMMENTS代替
顾名思义,开启后将支持Yaml格式的的注释,也就是#形式的注释语法。
ALLOW_UNQUOTED_FIELD_NAMES(false)
自2.10版本后,使用JsonReadFeature#ALLOW_UNQUOTED_FIELD_NAMES代替
是否允许属性名不带双引号"",比较简单,示例略。
ALLOW_SINGLE_QUOTES(false)
自2.10版本后,使用JsonReadFeature#ALLOW_SINGLE_QUOTES代替
是否允许属性名支持单引号,也就是使用''包裹,形如这样:
{ 'age' : 18 }
ALLOW_UNQUOTED_CONTROL_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数字,逗号、分号这些等等。
话外音:你肉眼能看到的字符就属于非控制字符
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(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。
ALLOW_NUMERIC_LEADING_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。
ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS(false)
自2.10版本后,使用
JsonReadFeature#ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS代替
是否允许小数点.打头,也就是说.1这种小数格式是否合法。默认是不合法的,需要开启此特征才能支持,例子就略了,基本同上。
ALLOW_NON_NUMERIC_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源码里的那几个常量