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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM