使用FastJson parseObject方法时,json字符串解析成对象后,部分属性丢失问题处理

出现此类问题的原因会有多种, 本文仅介绍发现的一种情况,不一定适用所有的场景

 

情景:

  JavaBean 中没有默认的构造方法

 

例如:

public class Student{


    public static void main(String[] args) {
        String jsonStr = "{\"id\":1,\"name\":\"Ming\",\"age\":18,\"phone\":\"23333333333\",\"address\":\"杭州\"}";
        Student student = JSON.parseObject(jsonStr, Student.class);
        System.out.println(JSON.toJSONString(student));
    }


    private Long id;
    private String name;
    private Integer age;
    private String address;
    private String phone;

    public Student(Long id, String name,String phone) {
        this.id = id;
        this.name = name;
        this.phone = phone;
    }


    public Student(Long id, String name, Integer age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

运行结果: {"address":"杭州","age":18,"id":1,"name":"Ming"}

此时解析成对象后,会丢失phone属性.虽然有一个构造器是带有phone字段的.

 

原因:

  FastJson 创建 JavaBean,调用了

#会调用以下构造方法
new JavaBeanDeserializer(this,clazz,type);

#构造方法详情
public JavaBeanDeserializer(ParserConfig config, Class<?> clazz, Type type){
        this(config //
                , JavaBeanInfo.build(clazz, type, config.propertyNamingStrategy, config.fieldBased, config.compatibleWithJavaBean, config.isJacksonCompatible())
        );
    }

#build方法确定使用哪个构造器的代码片段
for (Constructor constructor : constructors) {
                        Class<?>[] parameterTypes = constructor.getParameterTypes();

                        if (className.equals("org.springframework.security.web.authentication.WebAuthenticationDetails")) {
                            if (parameterTypes.length == 2 && parameterTypes[0] == String.class && parameterTypes[1] == String.class) {
                                creatorConstructor = constructor;
                                creatorConstructor.setAccessible(true);
                                paramNames = ASMUtils.lookupParameterNames(constructor);
                                break;
                            }
                        }

                        if (className.equals("org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken")) {
                            if (parameterTypes.length == 3
                                    && parameterTypes[0] == Object.class
                                    && parameterTypes[1] == Object.class
                                    && parameterTypes[2] == Collection.class) {
                                creatorConstructor = constructor;
                                creatorConstructor.setAccessible(true);
                                paramNames = new String[] {"principal", "credentials", "authorities"};
                                break;
                            }
                        }

                        if (className.equals("org.springframework.security.core.authority.SimpleGrantedAuthority")) {
                            if (parameterTypes.length == 1
                                    && parameterTypes[0] == String.class) {
                                creatorConstructor = constructor;
                                paramNames = new String[] {"authority"};
                                break;
                            }
                        }

                        //


                        boolean is_public = (constructor.getModifiers() & Modifier.PUBLIC) != 0;
                        if (!is_public) {
                            continue;
                        }
                        String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);
                        if (lookupParameterNames == null || lookupParameterNames.length == 0) {
                            continue;
                        }

                        if (creatorConstructor != null
                                && paramNames != null && lookupParameterNames.length <= paramNames.length) {
                            continue;
                        }

                        paramNames = lookupParameterNames;
                        creatorConstructor = constructor;
                    }

# 可以看到这句判断,会迭代当前JavaBean的所有构造器,并取到构造器方法列表最长的那个构造器,作为JSON解析的构造器.

if (creatorConstructor != null && paramNames != null && lookupParameterNames.length <= paramNames.length) {
    continue;
}


 

根据以上代码可以看出,为什么丢失了phone属性.方法形参列表最长的构造器是没有phone属性的

 

解决方法:

  1.新增默认的构造器

       2.方法形参列表最长的构造器中新增phone属性(如果代码已运行很久,建议新增构造器,方法形参列表最长,且包含phone字段)

 

posted @ 2020-04-03 12:46  yi杆烟枪  阅读(4737)  评论(0编辑  收藏  举报