集合裏有多種子類反序列化 子類屬性丟失問題 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);

        }
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章