fastjson 如何忽略@type类型解析

问题分析

最新使用Springboot redisTemplate遇到一个问题,自定义RedisSerializer中使用了fastjson进行序列化,调用JSON.toJSONString函数时添加了SerializerFeature.WriteClassName参数,导致反序列化的时候报错,JSONException: type not match.com.ruoyi.common.core.domain.entity.SysDictData -> com.ruoyi.system.domain.SysDictData,原因就是反序列化指定的类类型与序列化的时候不一致,虽然类名一样,但是全路径不一样。通常在不同的系统间进行交互,使用同一个redis集群中的资源时会出该问题。

解决方法

第一种方式

两个系统的同名称的类放到同一个package下,这样fastjson类型校验就会通过。或者就是在自定义RedisSerializer的时候,序列化不添加SerializerFeature.WriteClassName参数,这样多个系统才能共用

第二种方式

源码分析

自定义AutoTypeCheckHandler,进行全局配置
fastjson在进行反序列化的时候,如果解析到有@type类型,会调用ParserConfig.checkAutoType()方法进行校验

protected <T> T deserialze(DefaultJSONParser parser, // 
                               Type type, // 
                               Object fieldName, // 
                               Object object, //
                               int features, //
                               int[] setFlags) {
    /** 此处一大堆代码省略 */                           
	ObjectDeserializer deserializer = getSeeAlso(config, this.beanInfo, typeName);
	Class<?> userType = null;
	
	if (deserializer == null) {
	    Class<?> expectClass = TypeUtils.getClass(type);
	
		// 默认全局类型校验
	    if (autoTypeCheckHandler != null) {
	        userType = autoTypeCheckHandler.handler(typeName, expectClass, lexer.getFeatures());
	    }
		
	    if (userType == null) {
	        if (typeName.equals("java.util.HashMap") || typeName.equals("java.util.LinkedHashMap")) {
	            if (lexer.token() == JSONToken.RBRACE) {
	                lexer.nextToken();
	                break;
	            }
	            continue;
	        }
	    }
	
	    if (userType == null) {
	        // 最终会调用checkAutoType方法
	        userType = config.checkAutoType(typeName, expectClass, lexer.getFeatures());
	    }
	    deserializer = parser.getConfig().getDeserializer(userType);
	}
	/** 此处一大堆代码省略 */
}

public class ParserConfig {
    
    /** 类型校验 */
	public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
	    if (typeName == null) {
	        return null;
	    }
	
	    // 运行自定义的AutoTypeCheckHandler 
	    if (autoTypeCheckHandlers != null) {
	        for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {
	            // typeName:json字符串中@type指定的类型名称 如com.ruoyi.common.core.domain.entity.SysDictData
	            // expectClass:parseObject方法中指定的类名称JSON.parseObject(jsonData, com.ruoyi.system.domain.SysDictData.class)
	            // 只需要再自定义AutoTypeCheckHandler返回@type指定的类型,只要type不为空,直接返回,下面的处理逻辑就不走了
	            Class<?> type = h.handler(typeName, expectClass, features);
	            if (type != null) {
	                return type;
	            }
	        }
	    }
	    final int safeModeMask = Feature.SafeMode.mask;
        boolean safeMode = this.safeMode
                || (features & safeModeMask) != 0
                || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;
        if (safeMode) {
            throw new JSONException("safeMode not support autoType : " + typeName);
        }

        if (typeName.length() >= 192 || typeName.length() < 3) {
            throw new JSONException("autoType is not support. " + typeName);
        }
        /** 下面一大堆校验代码省略 */
	}
}

最终源码
import com.ruoyi.common.core.domain.entity.SysDictData;

public static void main(String[] args) {
    // 自定义类型检验器,直接把@type指定的类全名称返回即可,这样反序列化就不会出现类名称校验不一致的情况
	ParserConfig.getGlobalInstance().addAutoTypeCheckHandler((typeName, expectClass, features) -> {
	    try {
	        return Class.forName(typeName);
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	    return expectClass;
	});
	SysDictData sysDictData = new SysDictData();
	sysDictData.setDictLabel("测试");
	sysDictData.setDictValue("1");
	String jsonData = JSON.toJSONString(sysDictData, SerializerFeature.WriteClassName);
	System.out.println(jsonData);
	System.out.println(JSON.parseObject(jsonData, com.ruoyi.system.domain.SysDictData.class));
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值