先说结论
既然数据结构变了,同样也是个k-v的形式,手动写个反射解析一下即可。代码如下:
public Item convertUserFromLinkedHashMap(LinkedHashMap<String, Object> map) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Item item = new Item();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
String keyName = key.substring(0,1).toUpperCase() + key.substring(1);
String setter = "set" + keyName;
String getter = "get" + keyName;
item.getClass().getMethod(setter, item.getClass().getDeclaredMethod(getter).getReturnType())
.invoke(item, entry.getValue());
}
return item;
}
说说思路
问题背景
出现问题的代码形式如下:
@RequestMapping("edit")
public Response edit(@RequestBody Map<String, Object> params) {
Item item;
item = (Item) params.get("params");
return itemService.edit(item);
}
在使用上面的代码处理业务时,抛出ClassCastException,大致意思为无法将LinkedHashMap对象转换成Item对象。
前端传入的数据样例如下:
{"params":{"param1":"pararm1","param2":2}}
产生原因
大概是因为接收到的参数会默认存放进LinkedHashMap的子类ModelMap中,但在一定情况下直接返回了LinkedHashMap而没有进行封装。
可能解决方案
1.引入FastJSON解析输入数据(未解决问题)
从前端传递进来的数据是JSON格式,出现问题的数据结构也是一种K-V结构,很自然就想到了FastJSON来解决问题。
但其实忽略了一个问题,标准的JSON格式的key和value的格式为"key":"value"
,但在出现问题的LinkedHashMap中依然不符合JSON的格式,故FastJSON无法胜任工作。
2. 对于每个实体写一串if逻辑写入逻辑
是一种可以临时运行的方案,但对于程序的扩展与维护并不友好。样例代码如下:
public Item convertUserFromLinkedHashMap(LinkedHashMap<String, Object> map) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Item item = new Item();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if ("param1".equals(key)) {
item.setParam1(entry.getValue());
} else if("param2".equals(key)) {
item.setParam2(entry.getValue());
}
}
return item;
}
当然也可以写成switch-case的结构,但总体类似。
3. 使用反射的方式实现一个类似JSON解析的方法
其实在第二种解决方案中可以发现一个共性问题,通过key的值来进行判断,并set对应的属性,其实可以借助于Java的反射机制来完成工作。核心代码已在文章最初进行展示,这里不再赘述,这里只对代码思路进行一定的解释。
最终我们是要调用set方法,对于set方法有一个入参,诚然,可以反射可以获取方法列表的类型,但这需要我们先获取到对应方法才可以获取参数类型,此时我们并没有获取到set方法,所以直接获取参数类型的方案就需要进行变通。get方法通常和set方法成对出现,而get方法的返回值类型就是set方法的参数类型,可以通过这一特点获取get方法的返回参数当做set方法的参数类型,这样就完成了反射方法调用参数的声明。
至于如何获取set方法和get方法,这里利用了一个开发公约,即“set方法以set开头随后以对应属性的大驼峰表示法拼接形成方法名,get方法同理”,大驼峰和小驼峰的方法仅有首字母不同,故将首位字母设定为大写并在前面拼接set或者get生成方法名。
如此一来就获取到了对应的方法并可以进行调用。
遗留问题
- 对于问题成因部分存疑,后面会进行考证。
- 给出的方法返回值类型写定了,简单尝试过使用范型的方式,但范型不能进行new,或许还有其他实现方案,可以实现更加优雅的实现。