使用fastjson序列化后字段属性发生了变化

问题描述

使用 fastjson 进行 JSON 序列化存储到数据库后,发现 JSON 字符串“莫名其妙地”多了一些属性,也少了些属性。问题出现在基本类型的布尔类型以 is 开头的属性。

复现

1、定义对象
其中一个boolean类型的属性isActive以is开头,一个Integer 类型的属性isUse以is开头。

@Data
@Accessors(chain = true)
public class Test {
    private boolean isActive;
    private Integer isUse;
    private boolean valid;
    private Integer value;
}

2、使用fastjson序列化

在这里插入图片描述

3、测试结果
发现少了isActive没有正常返回,isUse正常返回了。
在这里插入图片描述

问题分析

通过调试可以发现,问题出现在下面这个函数:

com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer(java.lang.Class<?>)
publicfinal ObjectSerializer createJavaBeanSerializer(Class<?> clazz) {
    String className = clazz.getName();
    long hashCode64 = TypeUtils.fnv1a_64(className);
    if (Arrays.binarySearch(denyClasses, hashCode64) >= 0) {
        throw new JSONException("not support class : " + className);
    }

    // 关键
    SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBased);
    if (beanInfo.fields.length == 0 && Iterable.class.isAssignableFrom(clazz)) {
        return MiscCodec.instance;
    }

    return createJavaBeanSerializer(beanInfo);
}

而 buildBeanInfo 的关键是com.alibaba.fastjson.util.TypeUtils#computeGetters

public static List<FieldInfo> computeGetters(Class<?> clazz, //
                                             JSONType jsonType, //
                                             Map<String,String> aliasMap, //
                                             Map<String,Field> fieldCacheMap, //
                                             boolean sorted, //
                                             PropertyNamingStrategy propertyNamingStrategy //
){
    // 省略部分代码

        if(methodName.startsWith("is")){
            if(methodName.length() < 3){
                continue;
            }
            if(returnType != Boolean.TYPE
                    && returnType != Boolean.class){
                continue;
            }
            char c2 = methodName.charAt(2);
            String propertyName;
            Field field = null;
            if(Character.isUpperCase(c2)){
                if(compatibleWithJavaBean){
                    propertyName = decapitalize(methodName.substring(2));
                } else{
                    propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
                }
                // 这里 isActive 的属性名被计算出 active
                propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 2);
            } 

            // 省略其他

             JSONField fieldAnnotation = null;
            if(field != null){
                fieldAnnotation = TypeUtils.getAnnotation(field, JSONField.class);
                if(fieldAnnotation != null){
                    if(!fieldAnnotation.serialize()){
                        continue;
                    }
                    ordinal = fieldAnnotation.ordinal();
                    serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
                    parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
                    if(fieldAnnotation.name().length() != 0){
                        //关键:使用 JSONField 注解设置的 name 替代属性名
                        propertyName = fieldAnnotation.name();
                        if(aliasMap != null){
                            propertyName = aliasMap.get(propertyName);
                            if(propertyName == null){
                                continue;
                            }
                        }
                    }
                    if(fieldAnnotation.label().length() != 0){
                        label = fieldAnnotation.label();
                    }
                }
            }


            // 省略部分代码

            FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                    annotation, fieldAnnotation, label);
            fieldInfoMap.put(propertyName, fieldInfo);
        }
    }
    Field[] fields = clazz.getFields();
    computeFields(clazz, aliasMap, propertyNamingStrategy, fieldInfoMap, fields);
    return getFieldInfos(clazz, sorted, fieldInfoMap);
}

其实 fastjson 通过反射虽然有能力识别真实的属性名,但是实际操作时会根据 getter 方法反推出属性名,造成转为 JSON 字符串时和实际属性名存在偏差。

解决办法

1、遵循阿里巴巴 Java 开发手册
《Java 开发手册》 中专门强调任何布尔类型的变量都不要加 is 前缀,基本类型布尔属性反向解析时,会误以为不带 is 导致获取不到属性,抛出异常。
在这里插入图片描述
2、使用别名
使用 fastjson 自带的 @JSONField 注解,不过这种方式 fastjson 的侵入性太强。
在这里插入图片描述
3、使用hutool的JSONUtil.toJsonStr()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值