场景重现
项目中使用阿里fastjson
将字符串转java
对象时,出现部分属性没有赋值的情况,下面模拟该场景:
//Java目标对象
@Data
public class TestBean {
private String fieldA;
private String fieldB;
private String fieldC;
/**
* 构造方法(注意!不含属性C)
*/
public TestBean(String fieldA, String fieldB) {
this.fieldA = fieldA;
this.fieldB = fieldB;
}
}
//模拟转换方法
public static void main(String[] args) {
//模拟json字符串,包含 fieldC
String jsonStr = "{\"fieldA\":\"a\",\"fieldB\":\"B\",\"fieldC\":\"CC\"}";
//转 java Obj
TestBean obj = JSON.toJavaObject(JSON.parseObject(jsonStr), TestBean.class);
log.info("json: {}", obj);
}
期望输出TestBean
的所有属性,但实际上输出的是:
Demo.TestBean(fieldA=a, fieldB=B, fieldC=null)
- 问题:从输出的结果可以看到,缺失属性
fieldC
的值。
原因
转java
对象时,调用了类TestBean
中申明的只有2个参数的构造方法TestBean(String fieldA, String fieldB)
。
JSON.
toJavaObject(JSON json, Class<T> clazz)
源码分析
public static <T> T toJavaObject(JSON json, Class<T> clazz) {
return TypeUtils.cast(json, clazz, ParserConfig.getGlobalInstance());
}
public static <T> T cast(Object obj, Class<T> clazz, ParserConfig config){
//判断类型
if(obj == null){
if(clazz == int.class){
......
if(obj instanceof Map){
if(clazz == Map.class){
return (T) obj;
}
Map map = (Map) obj;
if(clazz == Object.class && !map.containsKey(JSON.DEFAULT_TYPE_KEY)){
return (T) obj;
}
//转java对象
return castToJavaBean((Map<String,Object>) obj, clazz, config);
}
......
}
public static <T> T castToJavaBean(Map<String,Object> map, Class<T> clazz, ParserConfig config){
try{
if(clazz == StackTraceElement.class){
String declaringClass = (String) map.get("className");
String methodName = (String) map.get("methodName");
String fileName = (String) map.get("fileName");
......
if (clazz == JSONObject.class) {
return (T) new JSONObject(map);
}
if (config == null) {
config = ParserConfig.getGlobalInstance();
}
JavaBeanDeserializer javaBeanDeser = null;
//获取解码器对象 ObjectDeserializer
// ObjectSerializer和ObjectDeserializer分别是fastjson的编码器和解码器接口。
ObjectDeserializer deserializer = config.getDeserializer(clazz);
......
}
public ObjectDeserializer getDeserializer(Type type) {
ObjectDeserializer deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
if (type instanceof Class<?>) {
//获取 解码器 对象
return getDeserializer((Class<?>) type, type);
}
......
}
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
ObjectDeserializer deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
......
if (clazz.isEnum()) {
......
else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);
} else if (clazz == InetAddress.class) {
deserializer = MiscCodec.instance;
} else {
//创建 解码器 对象
deserializer = createJavaBeanDeserializer(clazz, type);
}
......
}
public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {
boolean asmEnable = this.asmEnable & !this.fieldBased;
......
if (asmEnable) {
if (clazz.isInterface()) {
asmEnable = false;
}
//构建JavaBeanInfo对象
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz
, type
, propertyNamingStrategy
,false
, TypeUtils.compatibleWithJavaBean
, jacksonCompatible
);
......
}
public static JavaBeanInfo build(Class<?> clazz, Type type,......) {
JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);
......
//获取定义的构造器集合,实际只有一个,即:构造方法: public TestBean(String fieldA, String fieldB)
Constructor[] constructors = clazz.getDeclaredConstructors();
......
boolean isInterfaceOrAbstract = clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
if ((defaultConstructor == null && builderClass == null) || isInterfaceOrAbstract) {
creatorConstructor = getCreatorConstructor(constructors);
if (creatorConstructor != null && !isInterfaceOrAbstract) { // 基于标记 JSONCreator 注解的构造方法
......
} else if ((factoryMethod = getFactoryMethod(clazz, methods, jacksonCompatible)) != null) {
......
} else if (!isInterfaceOrAbstract) {
......
if (kotlin && constructors.length > 0) {
......
} else {
//关键代码,constructors只定义了一个含 fieldA、fieldB参数的构造器
for (Constructor constructor : constructors) {
//通过构造器获取属性,所以这里只能获取到fieldA、fieldB,而不能获取到 fieldC,因此造成后面赋值属性时fieldC的属性值缺失问题
Class<?>[] parameterTypes = constructor.getParameterTypes();
......
}
}
解决
TestBean
类增加一个无参构造器即可!