zoukankan      html  css  js  c++  java
  • JSON不对称反序列化映射方案

    源码Git地址: https://github.com/git-simm/simm-framework.git (欢迎大家提交优化代码 ^_^)
    一、业务场景
      公司先有业务系统,后来觉得需要抽离公共的底层权限服务。再加上之前的业务对象命名不规范,这次想要一次搞定。面对这种场景,摆在我面前的有三套方案。
    1. 用底层权限服务提供的数据格式,把业务代码中不规范的引用都改一遍。影响面实在太广,放弃;
    2. 加一个数据适配层,从底层权限服务请求到json数据,定义一套匹配的pojo类型进行接收。之后再用适配方法,进行对象转换,得到适合系统使用的业务对象。这种搞法比较传统,代理层、适配器都需要自己人工处理。代码量还是较大,不够优雅;
    3. 利用反射+注解的方式,让程序自动去匹配不对等的属性,自行完成数据适配的过程。这种搞法就便捷多了,以后遇到名称不匹配的属性,我就直接添加个注解就行了。接下来就开撸吧。
     
    二、实现目标
    1. 简单对象,属性都是简单类型,能够自动映射;
    2. 复杂的Class,要能递归进行自动映射;
    3. 复杂的List对象,要能递归进行自动映射;

    三、实现方案

    • 核心转换实现类 :ProxyJsonUtil
    • 映射关系解析实现类: ProxyResolveUtil
    • 反射赋值工具类:ReflectUtil
    • 权限平台请求代理:AuthorityUtil

    1. 新建两个注解 ProxyEntity、ProxyProp

    /**
     * 代理实体(用于json序列化&反序列化)
     */
    @Retention(value = RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface ProxyEntity {
        //代理实体名
        String value() default "";
    }
    
    /**
     * 代理属性(用于json序列化&反序列化)
     */
    @Retention(value = RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface ProxyProp {
        //属性名
        String value();
    }

    2、核心代码 反射+递归 进行字段映射

    package simm.framework.jsonext;
    
    import org.springframework.data.util.Pair;
    import simm.framework.jsonext.annotations.ProxyEntity;
    import simm.framework.jsonext.annotations.ProxyProp;
    import simm.framework.jsonext.entity.ProxyField;
    import simm.framework.jsonext.entity.ProxyMsg;
    
    import java.lang.reflect.Field;
    import java.util.*;
    
    /**
     * 代理信息解析工具
     */
    public class ProxyResolveUtil {
        /**
         * 同步锁
         */
        private static Object _lock = new Object();
        /**
         * 代理类型缓存
         */
        //对类型缓存做线程安全处理
        private static Map<String,ProxyMsg> clazzCache = Collections.synchronizedMap(new HashMap<String,ProxyMsg>());
    
        /**
         * 获取代理信息
         * @param clazz
         * @return
         */
        public static ProxyMsg getProxyMsg(Class clazz){
            String key = clazz.getName();
            if(clazzCache.containsKey(key)) return clazzCache.get(key);
            synchronized (_lock){
                //双重检查
                if(clazzCache.containsKey(key)) return clazzCache.get(key);
                //开始解析
                clazzCache.put(key,getClazzProxyMsg(clazz));
            }
            return clazzCache.get(key);
        }
    
        /**
         * 获取类型代理信息
         * @param clazz
         * @return
         */
        private static ProxyMsg getClazzProxyMsg(Class clazz){
            ProxyEntity proxyEntity = (ProxyEntity) clazz.getAnnotation(ProxyEntity.class);
            if(proxyEntity==null) return null;
            ProxyMsg proxyMsg = new ProxyMsg();
            proxyMsg.setClazz(clazz);
            proxyMsg.setProxyName(proxyEntity.value());
            //解析字段信息
            List<ProxyField> propList = new ArrayList<>();
            List<Pair<String, String>> listConfig= new ArrayList<>();
            HashMap<String, String> mapConfig= new HashMap<>();
            for (Field field: clazz.getDeclaredFields()){
                ProxyProp proxyProp = field.getAnnotation(ProxyProp.class);
                if(proxyProp == null){
                    mapConfig.put(field.getName(),field.getName());
                    continue;
                }else{
                    ProxyField pField = new ProxyField();
                    pField.setField(field);
                    pField.setFieldName(field.getName());
                    pField.setProxyName(proxyProp.value());
                    propList.add(pField);
                    //beanutils 做属性拷贝时,使用该参数
                    listConfig.add(Pair.of(pField.getProxyName(),pField.getFieldName()));
                    mapConfig.put(pField.getProxyName(),pField.getFieldName());
                }
            }
            proxyMsg.setFields(propList);
            proxyMsg.setListConfig(listConfig);
            proxyMsg.setMapConfig(mapConfig);
            return proxyMsg;
        }
    }
    View Code
    package simm.framework.jsonext;
    
    import java.lang.reflect.*;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    public class ReflectUtil {
        /**
         * 同步锁
         */
        private static Object _lock = new Object();
        /**
         * 代理字段缓存
         */
        //对类型缓存做线程安全处理
        private static Map<String,Field> fieldCache = Collections.synchronizedMap(new HashMap<String,Field>());
    
        /**
         * 获取字段信息
         * @param entity
         * @param fieldName
         * @param <E>
         * @return
         */
        public static <E> Field getField(E entity,String fieldName){
            String key = entity.getClass().getName()+"@"+fieldName;
            if(fieldCache.containsKey(key)) return fieldCache.get(key);
            synchronized (_lock){
                //双重检查
                if(fieldCache.containsKey(key)) return fieldCache.get(key);
                //开始解析
                Field f = null;
                try {
                    f = entity.getClass().getDeclaredField(fieldName);
                    f.setAccessible(true);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
                fieldCache.put(key,f);
            }
            return fieldCache.get(key);
        }
        /**
         * 获取list 属性的 泛型类型
         * @param entity
         * @param fieldName
         * @return
         */
        public static <E> Class<?> getActClazz(E entity, String fieldName){
            Field f = getField(entity,fieldName);
            if(f.getType() == java.util.List.class){
                // 如果是List类型,得到其Generic的类型
                Type genericType = f.getGenericType();
                if(genericType == null) return null;
                // 如果是泛型参数的类型
                if(genericType instanceof ParameterizedType){
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    Class<?> genericClazz = (Class<?>)pt.getActualTypeArguments()[0];
                    return genericClazz;
                }
            }
            return (Class<?>) f.getGenericType();
        }
        /**
         * 获取字段值
         * @param target
         * @param fieldName
         * @param <E>
         * @return
         * @throws Exception
         */
        public static <E> Object getFieldVal(E target, String fieldName){
            try {
                return getField(target,fieldName).get(target);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 设置字段值
         * @param target
         * @param fieldName
         * @param <E>
         * @return
         */
        public static <E> void setFieldVal(E target, String fieldName,Object value) {
            try {
                getField(target,fieldName).set(target,value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    View Code
    package simm.framework.jsonext;
    
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.parser.ParserConfig;
    import com.alibaba.fastjson.util.TypeUtils;
    import org.springframework.data.util.Pair;
    import simm.framework.jsonext.entity.ProxyMsg;
    
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class ProxyJsonUtil {
        /**
         * 批量拷贝
         * @param source
         * @param clazz
         * @param <E>
         * @param deepCopy 是否需要递归处理
         * @return
         */
        public static <E> List<E> cast(List<JSONObject> source, Class<E> clazz,boolean deepCopy) {
            List<E> result = new ArrayList<>();
            for (JSONObject t : source) {
                E object = cast(t, clazz, deepCopy);
                result.add(object);
            }
            return result;
        }
    
        /**
         * 单条拷贝
         * @param source
         * @param clazz
         * @param <E>
         * @return
         */
        public static <E> E cast(JSONObject source, Class<E> clazz, boolean deepCopy) {
            return copyProperties(source, clazz, deepCopy);
        }
    
        /**
         * 拷贝属性
         * @param source
         * @param clazz
         * @param <E>
         * @param deepCopy
         * @return
         */
        private static <E> E copyProperties(JSONObject source, Class<E> clazz, boolean deepCopy) {
            try {
                E object = TypeUtils.castToJavaBean(source, clazz, ParserConfig.getGlobalInstance());
                copyProperties(source, object, deepCopy);
                return object;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * JsonObject属性值拷贝
         * @param source
         * @param target
         * @param <E>
         * @param deepCopy
         */
        private static <E> void copyProperties(JSONObject source,E target, boolean deepCopy) throws Exception {
            if(deepCopy){
                //深层递归,处理子对象赋值
                proxyEntityCast(source, target, deepCopy);
            }
            //代理字段赋值
            proxyFieldCast(source, target);
        }
    
        /**
         * 深层递归,处理子对象赋值
         * @param source
         * @param target
         * @param deepCopy
         * @param <E>
         * @throws Exception
         */
        private static <E> void proxyEntityCast(JSONObject source, E target, boolean deepCopy) throws Exception {
            ProxyMsg msg = ProxyResolveUtil.getProxyMsg(target.getClass());
            Map<String,String> map = msg.getMapConfig();
            for (Map.Entry<String, Object> entry : source.entrySet()){
                //映射实体不包含该属性,则退出代理赋值
                if(!map.containsKey(entry.getKey()))continue;
                //获取映射实体的字段名
                String fieldName = map.get(entry.getKey());
                Object value = entry.getValue();
                if(value instanceof JSONArray){
                    jsonArrayCast(target, deepCopy, fieldName, (JSONArray) value);
                }else if(value instanceof JSONObject){
                    jsonObjectCast(target, deepCopy, fieldName, (JSONObject) value);
                }
            }
        }
    
        /**
         * JSONObject转换处理
         * @param target
         * @param deepCopy
         * @param fieldName
         * @param value
         * @param <E>
         * @throws Exception
         */
        private static <E> void jsonObjectCast(E target, boolean deepCopy, String fieldName, JSONObject value) throws Exception {
            //属性是一个JSONObject 对象
            Object fieldTarget = ReflectUtil.getFieldVal(target, fieldName);
            if(fieldTarget == null){
                Type fieldType = ReflectUtil.getField(target, fieldName).getGenericType();
                ReflectUtil.setFieldVal(target, fieldName,copyProperties(value,(Class)fieldType,deepCopy));
            }else{
                //递归为JsonObject属性赋值
                copyProperties(value,fieldTarget,deepCopy);
            }
        }
    
        /**
         * JSONArray 字段处理
         * @param target
         * @param deepCopy
         * @param fieldName
         * @param value
         * @param <E>
         * @throws Exception
         */
        private static <E> void jsonArrayCast(E target, boolean deepCopy, String fieldName, JSONArray value) throws Exception {
            //属性是一个 JSONArray 的列表对象
            //获取需要被赋值的目标对象
            Object fieldTarget = ReflectUtil.getFieldVal(target, fieldName);
            //目标对象为空,退出运行
            Class<?> fieldGenericClazz = ReflectUtil.getActClazz(target,fieldName);
            if(fieldTarget == null){
                fieldTarget = new ArrayList();
                ReflectUtil.setFieldVal(target, fieldName,fieldTarget);
            }
            JSONArray tempList = value;
            if(fieldTarget instanceof List){
                // 如果是List类型,得到其Generic的类型
                List<Object> temps = (List<Object>)fieldTarget;
                for(int i=0;i<tempList.size();i++){
                    //递归为JsonObject属性赋值
                    if(temps.size()<=i){
                        //自动创建新对象
                        temps.add(copyProperties((JSONObject)tempList.get(i),fieldGenericClazz,deepCopy));
                    }else{
                        copyProperties((JSONObject)tempList.get(i),temps.get(i),deepCopy);
                    }
                }
            }
        }
        /**
         * 代理字段赋值
         * @param source
         * @param target
         * @param <E>
         */
        private static <E> void proxyFieldCast(JSONObject source, E target) {
            //代理字段赋值
            ProxyMsg msg = ProxyResolveUtil.getProxyMsg(target.getClass());
            for (Pair<String, String> pair : msg.getListConfig()) {
                String key = pair.getFirst();
                if(!source.containsKey(key)) continue;
                Object value = source.get(key);
                if(value instanceof JSONArray || value instanceof JSONObject) continue;
                ReflectUtil.setFieldVal(target, pair.getSecond(),value);
            }
        }
    }
    View Code
  • 相关阅读:
    mysql 优化20点
    java function
    设计模式 概览
    Linux安装java1.8并配置环境变量
    windows下一次执行多个sql文件
    mybatis查询结果为空时的返回值问题
    Java中Json与String互转
    SSM Service自动注入失败
    本地安装Mysql5.7过程中出现的一系列问题
    解决本地工具无法连接服务器上的mysql的问题
  • 原文地址:https://www.cnblogs.com/MrSi/p/9452328.html
Copyright © 2011-2022 走看看