集合里有多种子类反序列化 子类属性丢失问题 Java代码实现[解决循环引用问题]

先放一段代码

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);

        }
    }

}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值