出現此類問題的原因會有多種, 本文僅介紹發現的一種情況,不一定適用所有的場景
情景:
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字段)