Fastjson源码分析—反序列化—Feature的功能和实现

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起作用的逻辑。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
fastjson 2.0.0版本提供了多种反序列化配置,可以通过配置来控制反序列化的行为,以增强安全性和灵活性。以下是常见的反序列化配置: 1. AutoType:开启该配置后,fastjson将支持反序列化任何Java类,包括未知的、不在白名单中的类。这个配置存在安全隐患,因为攻击者可以通过构造恶意的JSON字符串来执行任意代码。因此,建议仅在必要时才开启该配置,并通过白名单过滤掉非法类。 2. ParserConfig:该配置用于管理反序列化时的解析器配置,包括白名单、黑名单、自定义反序列化器等。通过ParserConfig可以实现更精细的反序列化控制。 3. Feature:该配置用于开启或关闭fastjson的一些特性,如自动类型匹配、允许注释、允许单引号等。通过Feature可以灵活地控制反序列化的行为。 4. ASM:该配置用于开启或关闭fastjson的ASM字节码增强功能,以提高反序列化性能。默认情况下,fastjson会尝试使用ASM来优化反序列化代码,但在某些环境下可能会造成兼容性问题。 5. SerializeConfig:该配置用于序列化时的配置管理,包括白名单、黑名单、自定义序列化器等。通过SerializeConfig可以实现更精细的序列化控制。 这些配置可以通过调用JSON类的静态方法来设置,例如: ``` JSON.DEFAULT_PARSER_FEATURE |= Feature.AutoCloseSource.getMask(); JSON.DEFAULT_PARSER_FEATURE &= ~Feature.UseBigDecimal.getMask(); ``` 注意,反序列化配置应该根据具体情况进行选择和设置,以确保安全和性能。建议仔细阅读fastjson的官方文档,并参考相关的安全规范和最佳实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值