【bug】【json】解决使用fastjson将字符串转JavaObj出现部分属性缺失问题

文章讲述了在使用阿里fastjson将包含所有字段的JSON字符串转换为Java对象时,由于目标类TestBean缺少无参构造器,导致部分属性如fieldC未被正确赋值。解决方法是为TestBean添加一个无参构造器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景重现

项目中使用阿里fastjson将字符串转java对象时,出现部分属性没有赋值的情况,下面模拟该场景:

	//Java目标对象
	@Data
    public class TestBean {
        private String fieldA;
        private String fieldB;
        private String fieldC;

        /**
         * 构造方法(注意!不含属性C)
         */
        public TestBean(String fieldA, String fieldB) {
            this.fieldA = fieldA;
            this.fieldB = fieldB;
        }
    }

	//模拟转换方法
	public static void main(String[] args) {
        //模拟json字符串,包含 fieldC
        String jsonStr = "{\"fieldA\":\"a\",\"fieldB\":\"B\",\"fieldC\":\"CC\"}";

        //转 java Obj
        TestBean obj = JSON.toJavaObject(JSON.parseObject(jsonStr), TestBean.class);
        log.info("json: {}", obj);
    }

期望输出TestBean 的所有属性,但实际上输出的是

Demo.TestBean(fieldA=a, fieldB=B, fieldC=null)
  • 问题:从输出的结果可以看到,缺失属性 fieldC的值。

原因

java对象时,调用了类TestBean申明的只有2个参数的构造方法TestBean(String fieldA, String fieldB)

  • JSON. toJavaObject(JSON json, Class<T> clazz)源码分析
	public static <T> T toJavaObject(JSON json, Class<T> clazz) {
        return TypeUtils.cast(json, clazz, ParserConfig.getGlobalInstance());
    }

	 public static <T> T cast(Object obj, Class<T> clazz, ParserConfig config){
	 	//判断类型
        if(obj == null){
            if(clazz == int.class){
            ......
        if(obj instanceof Map){
            if(clazz == Map.class){
                return (T) obj;
            }

            Map map = (Map) obj;
            if(clazz == Object.class && !map.containsKey(JSON.DEFAULT_TYPE_KEY)){
                return (T) obj;
            }
            //转java对象
            return castToJavaBean((Map<String,Object>) obj, clazz, config);
        }          
        ......
	}  

	    public static <T> T castToJavaBean(Map<String,Object> map, Class<T> clazz, ParserConfig config){
        try{
            if(clazz == StackTraceElement.class){
                String declaringClass = (String) map.get("className");
                String methodName = (String) map.get("methodName");
                String fileName = (String) map.get("fileName");
                ......

            if (clazz == JSONObject.class) {
                return (T) new JSONObject(map);
            }

            if (config == null) {
                config = ParserConfig.getGlobalInstance();
            }

            JavaBeanDeserializer javaBeanDeser = null;
            //获取解码器对象 ObjectDeserializer
            //	ObjectSerializer和ObjectDeserializer分别是fastjson的编码器和解码器接口。
            ObjectDeserializer deserializer = config.getDeserializer(clazz);
            ......
    }            

    public ObjectDeserializer getDeserializer(Type type) {
        ObjectDeserializer deserializer = get(type);
        if (deserializer != null) {
            return deserializer;
        }

        if (type instanceof Class<?>) {
            //获取 解码器 对象
            return getDeserializer((Class<?>) type, type);
        }
        ......

    }

    public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
        ObjectDeserializer deserializer = get(type);
        if (deserializer != null) {
            return deserializer;
        }
        ......
        if (clazz.isEnum()) {
           ......
        else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
            deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);
        } else if (clazz == InetAddress.class) {
            deserializer = MiscCodec.instance;
        } else {
            //创建 解码器 对象
            deserializer = createJavaBeanDeserializer(clazz, type);
        }
        ......
    }

    public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {
        boolean asmEnable = this.asmEnable & !this.fieldBased;
        ......
        if (asmEnable) {
            if (clazz.isInterface()) {
                asmEnable = false;
            }
            //构建JavaBeanInfo对象
            JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz
                    , type
                    , propertyNamingStrategy
                    ,false
                    , TypeUtils.compatibleWithJavaBean
                    , jacksonCompatible
            );
            ......
    }

    public static JavaBeanInfo build(Class<?> clazz, Type type,......) {
        JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);
        ......
        
        //获取定义的构造器集合,实际只有一个,即:构造方法: public TestBean(String fieldA, String fieldB) 
        Constructor[] constructors = clazz.getDeclaredConstructors();       
        ......
        boolean isInterfaceOrAbstract = clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
        if ((defaultConstructor == null && builderClass == null) || isInterfaceOrAbstract) {
            creatorConstructor = getCreatorConstructor(constructors);
            if (creatorConstructor != null && !isInterfaceOrAbstract) { // 基于标记 JSONCreator 注解的构造方法
                ......
            } else if ((factoryMethod = getFactoryMethod(clazz, methods, jacksonCompatible)) != null) {
                ......
            } else if (!isInterfaceOrAbstract) {
                ......
                if (kotlin && constructors.length > 0) {
                   ......
                } else {
                    //关键代码,constructors只定义了一个含 fieldA、fieldB参数的构造器
                    for (Constructor constructor : constructors) {
                        //通过构造器获取属性,所以这里只能获取到fieldA、fieldB,而不能获取到 fieldC,因此造成后面赋值属性时fieldC的属性值缺失问题
                        Class<?>[] parameterTypes = constructor.getParameterTypes();
                        ......
                    }
    }

解决

TestBean类增加一个无参构造器即可!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值