使用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字段)