先放一段代码
public class Test {
public static void main(String[] args) {
//建立一个数组 第一个放入child 第二个放入parent
List<Parent> list = new ArrayList<>();
Parent parent = new Parent();
parent.setX("1");
Child child = new Child();
child.setX1("1");
list.add(child);
list.add(parent);
String json = JSON.toJSONString(list);
List<Parent> list1 = JSON.parseArray(json, Parent.class);
//强转失败
Child child1 = (Child) list1.get(0);
}
}
显然List 里放入多种子类的话 子类类型会丢失 但是项目里有时候会有需求要把子类的信息完整得还原出来
实现思路如下: 在父类的最顶层里加一个classInfo,去标明类信息,子类都继承这个父类,然后序列化的时候拿map去接收 最后将map
当做一颗树 ,map如果带classInfo 就将他转化成classInfo的类,如果map里面还有map同理,层层递归
当然要考虑对象循环引用不能死递归的问题:
如果这个map已经被转成对象 那么他会被记在map里key是map value是map对应的对象
循环引用时取对象的hashcode会报错,所以用map当key会栈溢出 (最后放栈溢出的代码)
所以用System.identityHashCode(map) (可以看作map的内存地址)来当key
核心代码如下
/**
* 对参数做树递归 有条件用栈做
*
* @param object
* @param remember
* @return
*/
private static Object map2Class(Object object, Map<Integer, Object> remember) {
if (object instanceof List) {
List obj = new ArrayList();
List list = (List) object;
list.forEach(i -> {
obj.add(map2Class(i, remember));
});
return obj;
} else if (object instanceof Map) {
Map<String, Object> map = (Map<String, Object>) object;
Object classInfo = map.remove("className");
Object o = null;
if (classInfo == null) {
return Optional.ofNullable(remember.get(System.identityHashCode(map))).orElseGet(
() -> {
for (Map.Entry<String, Object> entry : map.entrySet()) {
map.put(entry.getKey(), map2Class(entry.getValue()));
}
return map;
}
);
} else {
try {
Class<?> aClass = Class.forName(classInfo.toString());
o = aClass.getDeclaredConstructor().newInstance();
remember.put(System.identityHashCode(map), o);
} catch (Exception e) {
e.printStackTrace();
}
}
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() != null) {
Object o1 = map2Class(entry.getValue(), remember);
reflectUtil.setProperty(o, entry.getKey(), o1);
}
}
return o;
} else {
return object;
}
}
/**
* 调用类
* @param object
* @param <T>
* @return
*/
public static <T> T map2Class(Object object) {
Map<Integer, Object> map = new HashMap<>();
Object o = map2Class(object, map);
return (T) o;
}
map入参如下:
转化后
栈溢出的代码如下:
public static void main(String[] args) {
Map<String,Map> map=new HashMap<>();
Map<String,Map> map1=new HashMap<>();
map.put("1",map1);
map1.put("1",map);
Set<Map> set=new HashSet<>();
//栈溢出底层用了hashcode
set.add(map);
}
反射工具类代码 对时间做了下特殊处理 缓存了反射的方法信息
public class GetSetterReflectUtil {
private final static Logger logger = LoggerFactory.getLogger(GetSetterReflectUtil.class);
private static WeakHashMap<SoftReference<String>, Map<String, Tuple3<Class, Method, Method>>> methodWeakHashMap = new WeakHashMap<>();
private static Map<Class, Function<Object, Object>> classFunctionMap = new HashMap<>();
static {
classFunctionMap.put(Date.class, (o) -> {
if (o instanceof Long) {
o = new Date((Long) o);
}
if (o instanceof String) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
Date parse = null;
try {
parse = sdf.parse(o.toString());
return parse;
} catch (ParseException e) {
logger.error("时间转化出现错误 格式不匹配 value[{}]", o);
}
}
return o;
});
}
private static Map<String, Tuple3<Class, Method, Method>> getClassMethod(Object beanObj) {
Class<?> aClass = beanObj.getClass();
String clazzName = aClass.getName();
PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(beanObj.getClass());
Map<String, Tuple3<Class, Method, Method>> map = new HashMap<>();
for (PropertyDescriptor descriptor : propertyDescriptors) {
String name = descriptor.getName();
Method readMethod = descriptor.getReadMethod();
Method writeMethod = descriptor.getWriteMethod();
map.put(name, Tuple3.of(descriptor.getPropertyType(), readMethod, writeMethod));
}
SoftReference<String> softReference = new SoftReference<String>(clazzName);
methodWeakHashMap.put(softReference, map);
return map;
}
public Object invokeMethod(Object beanObj, String methodName, String type, Object param) {
Class<?> aClass = beanObj.getClass();
String clazzName = aClass.getName();
SoftReference<String> softReference = new SoftReference<String>(clazzName);
Map<String, Tuple3<Class, Method, Method>> methodMap = methodWeakHashMap.get(softReference);
if (methodMap == null) {
methodMap = getClassMethod(beanObj);
methodWeakHashMap.put(softReference, methodMap);
}
Tuple3<Class, Method, Method> tuple3 = methodMap.get(methodName);
try {
Method method = "R".equals(type) ? tuple3.getT() : tuple3.getValue();
if (method == null) {
logger.error("未找到 name=[{}]的方法", methodName);
} else {
method.setAccessible(true);
Function<Object, Object> function = classFunctionMap.get(tuple3.getKey());
if (function != null) {
param = function.apply(param);
}
return method.invoke(beanObj, param);
}
} catch (Exception e) {
logger.error("执行 name=[{}] kClass=[{}] value=[{}] 的方法出错 {}", methodName, tuple3.getKey(), param, e);
throw new RuntimeException(e);
}
return null;
}
/* 该方法用于传入某实例对象以及对象方法名,通过反射调用该对象的某个get方法 */
public Object getProperty(Object beanObj, String methodName) {
try {
return invokeMethod(beanObj, methodName, "R", null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/* 该方法用于传入某实例对象以及对象方法名、修改值,通过放射调用该对象的某个set方法设置修改值 */
public void setProperty(Object beanObj, String methodName,
Object value) {
if (beanObj == null || methodName == null || value == null) {
return;
}
try {
invokeMethod(beanObj, methodName, "W", value);
} catch (Exception e) {
logger.error("执行set出现错误 [{}]", e);
}
}
}