使用 fastjson 的坑,我在加一条~

发布时间:2025-05-19 01:51:21 作者:益华网络 来源:undefined 浏览量(0) 点赞(0)
摘要:原文:juejin.cn/post/7156428078061895710 有一位同事说使用 fastjson 进行 JSON 序列化存储到数据库后,发现 JSON 字符串“莫名其妙地”多了一些属性!帮看了下代码,看到基本类型的布尔类型以 is 开头的属性,再看到 fastjson ,就有点想笑。

原文:juejin.cn/post/7156428078061895710

有一位同事说使用 fastjson 进行 JSON 序列化存储到数据库后,发现 JSON 字符串“莫名其妙地”多了一些属性!帮看了下代码,看到基本类型的布尔类型以 is 开头的属性,再看到 fastjson ,就有点想笑。

复现

定义 MyClass

public class MyClass {

    // boolean 类型的属性

    private boolean isActive;

    private boolean valid;

    // int 类型的属性

    private int id;

    // 默认构造器

    public MyClass

() {

    }

    // 带有所有属性的构造器

    public MyClass(boolean isActive, boolean valid, int id) {

        this.isActive = isActive;

        this.valid = valid;

        this.id = id;

    }

    // isActive 的 getter 和 setter 方法

    public boolean isActive

() {

        return

 isActive;

    }

    public void setActive(boolean isActive) {

        this.isActive = isActive;

    }

    // valid 的 getter 和 setter 方法

    public boolean getValid

() {

        return

 valid;

    }

    public void setValid(boolean valid) {

        this.valid = valid;

    }

    // id 的 getter 和 setter 方法

    public int getId

() {

        return

 id;

    }

    public void setId(int id) {

        this.id = id;

    }

}

编写测试代码:

import com.alibaba.fastjson.JSON;

public class MyClassMain {

    public static void main(String[] args) {

        // 创建 MyClass 对象

        MyClass myClass = new MyClass(truefalse

, 123);

        // 使用 fastjson 序列化对象

        String jsonString = JSON.toJSONString(myClass);

        // 打印 JSON 字符串

        System.out.println(jsonString);

    }

}

结果:

{“active”:true,“id”:123,“valid”:false

}

我们发现多了一个 active 属性,少了一个 isActive 属性!

分析

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

com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer(java.lang.Class<?>)

public final 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 computeGetters(Class  clazz, //

                                             JSONType jsonType, //

                                             Map aliasMap, //

                                             Map 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 字符串时和实际属性名存在偏差。

解决办法

遵循阿里巴巴 Java 开发手册

孤尽老师的《Java 开发手册》 中专门强调任何布尔类型的变量都不要加 is 前缀,基本类型布尔属性反向解析时,会误以为不带 is 导致获取不到属性,抛出异常。

使用别名

使用 fastjson 自带的 @JSONField 注解,不过这种方式 fastjson 的侵入性太强。

public class MyClass {

    @JSONField( name="isActive"

)

    // boolean 类型的属性

    private boolean isActive;

    private boolean valid;

    // 省略其他

}

总结

我认为 对于 Java 程序员而言,《阿里巴巴 Java 开发手册》至少读 3 遍。 工作中发现的很多常见低级问题都是 《阿里巴巴 Java 开发手册》已经存在的问题,建议大家认真阅读几遍。

而且我们遇到问题时,一定不要止步于解决当下问题,应该寻找最合理的解决方案。比如虽然加上 @JSONField 可以“解决问题”,但侵入性太强,假如其他人也用这个对象使用其他 JSON 序列化工具,就会出问题,这并不是一个好的方案。

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!