一、问题场景
目前我有一个类如下所示
@Data
@ToString
class BTEST {
Long infoId ;
Long info_id;
}
需要执行的方法如下
@Test
public void jsonToBean(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("infoid",100L);
map.put("info_id",null);
JSONObject jsonObject = new JSONObject(map);
BTEST object = JSONObject.toJavaObject(jsonObject, BTEST.class);
System.out.println(object);
}
我期望得到的结果是
BTEST(infoId=100, info_id=null)
实际运行结果却是
BTEST(infoId=null, info_id=null)
二、问题分析
查看fastjson的源码,如下所示
public static <T> T castToJavaBean(Map<String, Object> map, Class<T> clazz, ParserConfig config) {
//省略部分代码
JavaBeanDeserializer javaBeanDeser = null;
//获取对象反序列化器
ObjectDeserializer deserializer = config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
javaBeanDeser = (JavaBeanDeserializer) deserializer;
}
//创建类实例并赋值
return (T) javaBeanDeser.createInstance(map, config);
}
public Object createInstance(Map<String, Object> map, ParserConfig config) //
throws IllegalArgumentException,
IllegalAccessException,
InvocationTargetException {
Object object = null;
if (beanInfo.creatorConstructor == null && beanInfo.factoryMethod == null) {
//创建空实例
object = createInstance(null, clazz);
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
//smartMatch这个方法主要用来查找key值对应的参数反序列化器
FieldDeserializer fieldDeser = smartMatch(key);
if (fieldDeser == null) {
continue;
}
final FieldInfo fieldInfo = fieldDeser.fieldInfo;
Field field = fieldDeser.fieldInfo.field;
Type paramType = fieldInfo.fieldType;
//省略部分代码
String format = fieldInfo.format;
if (format != null && paramType == Date.class) {
//省略部分代码
} else {
if (paramType instanceof ParameterizedType) {
value = TypeUtils.cast(value, (ParameterizedType) paramType, config);
} else {
value = TypeUtils.cast(value, paramType, config);
}
}
//为参数赋值
fieldDeser.setValue(object, value);
}
return object;
}
//省略部分代码
}
public FieldDeserializer smartMatch(String key, int[] setFlags) {
if (key == null) {
return null;
}
//从正常的反序列化器中查找
FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);
if (fieldDeserializer == null) {
if (this.smartMatchHashArray == null) {
//基于正常的反序列化器生成一个用于智能匹配的hash数组
long[] hashArray = new long[sortedFieldDeserializers.length];
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
hashArray[i] = sortedFieldDeserializers[i].fieldInfo.nameHashCode;
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}
// smartMatchHashArrayMapping
//使用key值生成用于匹配的hashc
long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
//省略部分代码
if (pos >= 0) {
if (smartMatchHashArrayMapping == null) {
//生成key值哈希对应匹配的反序列化器的下标
short[] mapping = new short[smartMatchHashArray.length];
Arrays.fill(mapping, (short) -1);
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
int p = Arrays.binarySearch(smartMatchHashArray, sortedFieldDeserializers[i].fieldInfo.nameHashCode);
if (p >= 0) {
mapping[p] = (short) i;
}
}
smartMatchHashArrayMapping = mapping;
}
int deserIndex = smartMatchHashArrayMapping[pos];
if (deserIndex != -1) {
if (!isSetFlag(deserIndex, setFlags)) {
fieldDeserializer = sortedFieldDeserializers[deserIndex];
}
}
}
if (fieldDeserializer != null) {
FieldInfo fieldInfo = fieldDeserializer.fieldInfo;
if ((fieldInfo.parserFeatures & Feature.DisableFieldSmartMatch.mask) != 0) {
return null;
}
Class fieldClass = fieldInfo.fieldClass;
if (is && (fieldClass != boolean.class && fieldClass != Boolean.class)) {
fieldDeserializer = null;
}
}
}
return fieldDeserializer;
}
(标注错了,匹配infoid时正确结果应该是{0,-1})
查看sortedFieldDeserializers中对应的hash,发现两个反序列化器对应参数的hash是一模一样的
nameHashCode是在FieldInfo中生成的,如下所示
追踪到以下方法
private long nameHashCode64(String name, JSONField annotation)
{
if (annotation != null && annotation.name().length() != 0) {
return TypeUtils.fnv1a_64_lower(name);
}
return TypeUtils.fnv1a_64_extract(name);
}
其中fnv1a_64_lower 和 **fnv1a_64_extract 的逻辑几乎一模一样,唯一不同的是fnv1a_64_extract _**多了过滤下划线和横杆的逻辑,**nameHashCode64 **默认走的是 **fnv1a_64_extract **逻辑
通过分析 nameHashCode64 方法可以发现只需要加上@JSONField 注解让nameHashCode64方法走fnv1a_64_lower 的逻辑即可
三、解决方案
为info_id添加@JSONField(name = “info_id”)即可
@Data
@ToString
class BTEST {
Long infoId;
@JSONField(name = "info_id")
Long info_id;
}
输出结果
BTEST(infoId=100, info_id=null)