admin管理员组文章数量:1650765
2021SC@SDUSC
Feature的功能和实现
当我们对json字符串进行反序列化时,有时并不想完全按照json字符串默认的规则生成相应的Java对象,而有时我们手中的字符串亦不符合json字符串的格式,无法按照原有的规则进行解析。这时,我们需要修改反序列化的默认解析规则,而Fastjson恰好提供了这一功能。
使用Fastjson进行反序列化的时候,有一个可选的参数features,用于对反序列化的过程和结果进行定制化。
Feature的取值表示
先看看这几个重载方法:
public static Object parse(String text, ParserConfig config, Feature... features) {
int featureValues = DEFAULT_PARSER_FEATURE;
for (Feature feature : features) {
featureValues = Feature.config(featureValues, feature, true);
}
return parse(text, config, featureValues);
}
public static Object parse(String text, ParserConfig config, int features) {
if (text == null) {
return null;
}
DefaultJSONParser parser = new DefaultJSONParser(text, config, features);
Object value = parser.parse();
parser.handleResovleTask(value);
parser.close();
return value;
}
可以看到,这两个方法唯一的区别在于参数列表的不同,第一个方法允许传入多个features参数,而第二个方法只允许传入一个features。然而奇怪的是第一个方法调用了第二个方法,且只通过一个int类型的features就代表了多个参数,这一步怎么实现的?
接着我们跟进Feature.config()方法,看看它是如何把多个参数合并成一个int类型的变量来进行表示的。
public static int config(int features, Feature feature, boolean state) {
if (state) {
features |= feature.mask;
} else {
features &= ~feature.mask;
}
return features;
}
显然,该方法通过一个状态位state来对枚举类Feature的所有取值进行标识,例如,如果传入参数包含Feature.AllowComment,则对其标识为true,并将features和该数值进行二进制或操作,否则进行与操作。最后,features这个int型的数值已经与所有取值进行运算,得到的是一个二进制数,能够表示Feature的所有枚举情况。
Feature的功能
这里列举出枚举类Feature的所有对象,一一陈述其功能。
/**
*
*/
AutoCloseSource,
/**
*
*/
AllowComment,
/**
*
*/
AllowUnQuotedFieldNames,
/**
*
*/
AllowSingleQuotes,
/**
*
*/
InternFieldNames,
/**
*
*/
AllowISO8601DateFormat,
/**
* {"a":1,,,"b":2}
*/
AllowArbitraryCommas,
/**
*
*/
UseBigDecimal,
/**
* @since 1.1.2
*/
IgnoreNotMatch,
/**
* @since 1.1.3
*/
SortFeidFastMatch,
/**
* @since 1.1.3
*/
DisableASM,
/**
* @since 1.1.7
*/
DisableCircularReferenceDetect,
/**
* @since 1.1.10
*/
InitStringFieldAsEmpty,
/**
* @since 1.1.35
*
*/
SupportArrayToBean,
/**
* @since 1.2.3
*
*/
OrderedField,
/**
* @since 1.2.5
*
*/
DisableSpecialKeyDetect,
/**
* @since 1.2.9
*/
UseObjectArray,
/**
* @since 1.2.22, 1.1.54.android
*/
SupportNonPublicField,
/**
* @since 1.2.29
*
* disable autotype key '@type'
*/
IgnoreAutoType,
/**
* @since 1.2.30
*
* disable field smart match, improve performance in some scenarios.
*/
DisableFieldSmartMatch,
/**
* @since 1.2.41, backport to 1.1.66.android
*/
SupportAutoType,
/**
* @since 1.2.42
*/
NonStringKeyAsString,
/**
* @since 1.2.45
*/
CustomMapDeserializer,
/**
* @since 1.2.55
*/
ErrorOnEnumNotMatch,
/**
* @since 1.2.68
*/
SafeMode,
/**
* @since 1.2.72
*/
TrimStringFieldValue,
/**
* @since 1.2.77
* use HashMap instead of JSONObject, ArrayList instead of JSONArray
*/
UseNativeJavaObject
AutoCloseSource
:这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源
AllowComment
:该特性决定parser将是否允许解析使用Java/C++ 样式的注释
AllowUnQuotedFieldNames
:这个特性决定parser是否将允许使用非双引号属性名字。JavaScript中允许单引号作为属性名,但是json标准中不允许这样
AllowSingleQuotes
:该特性决定parser是否允许单引号来包住属性名称和字符串值。默认关闭
InternFieldNames
:该特性决定JSON对象属性名称是否可以被String#intern 规范化表示。
AllowISO8601DateFormat
:这个设置为true则遇到字符串符合ISO8601格式的日期时,会直接转换成日期类。
AllowArbitraryCommas
:允许多重逗号,如果设为true,则遇到多个逗号会直接跳过
UseBigDecimal
:这个设置为true则用BigDecimal类来装载数字,否则用的是double
SupportArrayToBean
:支持数组to对象
DisableASM
:DisableASM
UseObjectArray
:使用对象数组
Feature的实现
现在我们看一下Feature对象是在哪里起作用的。
DefaultJSONParser类里面有一个
public final Object parseObject(Map object, Object fieldName)
方法,其方法体太多就不全部粘贴过来了。这个方法里面有这么几行代码:
if (!lexer.isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}
这里很好的解释了AllowSingleQuotes这个对象起作用的方式。如果设置其为true,那么Fastjson会按照原有的解析方式解析字符串并声称对象。(上一节讲token的时候说明了Fastjson如何进行词法分析,且其实现时原本就支持单引号作为变量名)。如果设置其为false,一旦json字符串出现单引号作为变量名的情况,就会抛出语法错误的异常。
这只是一个bool类型的Feature的起作用方式,行为比较简单。下面我们来分析UseBigDecimal是如何起作用的。
仍然是这个方法:
public final Object parseObject(Map object, Object fieldName)
注意到其中有这一行代码:
value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
我们看看decimalValue()方法的结构:
public final Number decimalValue(boolean decimal) {
char chLocal = this.charAt(this.np + this.sp - 1);
try {
if (chLocal == 'F') {
return Float.parseFloat(this.numberString());
} else if (chLocal == 'D') {
return Double.parseDouble(this.numberString());
} else {
return (Number)(decimal ? this.decimalValue() : this.doubleValue());
}
} catch (NumberFormatException var4) {
throw new JSONException(var4.getMessage() + ", " + this.info());
}
}
显然,如果我们使用了Feature.UseBigDecimal,那么lexer.isEnable()会返回true,进而处理数字时,会用BigDecimal类来装载数字,否则使用double类来装载数字。
其他Feature就不一一分析了,可以明确的是这些配置信息都在DefaultJSONParser类里面起作用。它们的通用逻辑是一旦使用了某些Feature,会在lexer类里面用一个二进制的int变量表示所有标明了的Feature,随后在解析类里面讲这些Feature用布尔类型表示,通过if语句控制解析是是否抛出异常或者是否调用某些专用的解析类。
总结
Feature是Fastjson提供的一个非常重要的功能。适当地对Feature进行调整,可以定制用户反序列化的细节,比如是否允许使用单引号表示变量名和是否要用BigDecimal类对double类型的数字进行装填。Feature的实现比较隐秘,我debug了很长时间才找到它们起作用的代码段,最后才弄清楚Feature起作用的逻辑。
版权声明:本文标题:Fastjson源码分析—反序列化—Feature的功能和实现 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1729530941a1204796.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论