源码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;
}
}
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();
}
}
}
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);
}
}
}

JSON不对称反序列化映射方案的更多相关文章

  1. C# JSON 序列化和反序列化——JavaScriptSerializer实现

    一. JavaScriptSerializer 类由异步通信层内部使用,用于序列化和反序列化在浏览器和 Web 服务器之间传递的数据.您无法访问序列化程序的此实例.但是,此类公开了公共 API.因此, ...

  2. 序列化对象C++对象的JSON序列化与反序列化探索

    新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,欢迎批评指正 一:背景 作为一名C++开发人员,我始终很期待能够像C#与JAVA那样,可以省力的进行对象的序列化与反序列化,但到现在为止,还没有找 ...

  3. C++对象的JSON序列化与反序列化探索

    一:背景 作为一名C++开发人员,我一直很期待能够像C#与JAVA那样,可以轻松的进行对象的序列化与反序列化,但到目前为止,尚未找到相对完美的解决方案. 本文旨在抛砖引玉,期待有更好的解决方案:同时向 ...

  4. Net中JSON序列化和反序列化处理(日期时间特殊处理)

    0  缘由 笔者最近在web api端使用Json.Net进行序列化处理,而在调用端使用DataContractSerializer进行反序列化,遇到日期时间处理反序列化不成功[备注:笔者使用Net ...

  5. C++对象的JSON序列化与反序列化探索完结-列表的序列化与反序列化

    在前两篇文章中,我们已经完成对普通对象以及复杂对象嵌套的序列化与反序列化,见如下地址: C++对象的JSON序列化与反序列化探索 C++对象的JSON序列化与反序列化探索续-复杂对象的序列化与反序列化 ...

  6. C++对象的JSON序列化与反序列化探索续-复杂对象的序列化与反序列化

    本文是基本上一篇博文进行改进而成,上一篇请见: C++对象的JSON序列化与反序列化探索 此处就不多说了,直接上代码. 1. 序列化基类 #pragma once #include <strin ...

  7. 迄今为止 .Net 平台功能最强大,性能最佳的 JSON 序列化和反序列化库。

    Swifter.Json 这是迄今为止 .Net 平台功能最强大,性能最佳的 JSON 序列化和反序列化库. Github : https://github.com/Dogwei/Swifter.Js ...

  8. Newtonsoft.Json C# Json序列化和反序列化工具的使用、类型方法大全 C# 算法题系列(二) 各位相加、整数反转、回文数、罗马数字转整数 C# 算法题系列(一) 两数之和、无重复字符的最长子串 DateTime Tips c#发送邮件,可发送多个附件 MVC图片上传详解

    Newtonsoft.Json C# Json序列化和反序列化工具的使用.类型方法大全   Newtonsoft.Json Newtonsoft.Json 是.Net平台操作Json的工具,他的介绍就 ...

  9. DotNet的JSON序列化与反序列化

    JSON(JavaScript Object Notation)JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式.在现在的通信中,较多的采用JSON数据格式,JSON有 ...

随机推荐

  1. 微软发布WCF教程及大量示例

    继前面 微软公司发布Windows Communication Foundation (WCF)和Windows CardSpace的示例程序之后,微软今天又发布了WF的教程和大量示例,对于学习WF的 ...

  2. JMeter接口学习笔记2017

    协议学习地址:http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html 本篇学习笔记来自于慕课网上学习JMeter的学习笔记 学习 ...

  3. PHP echo汉字出现乱码的情况之一

    当然首先要理清楚到底是web服务端造成,php造成的,还是数据库造成的. 需要在头文件里UTF-8 <head> <meta http-equiv="Content-Typ ...

  4. C语言回调函数详解

    1. 什么是回调函数? 回调函数,光听名字就比普通函数要高大上一些,那到底什么是回调函数呢?恕我读得书少,没有在那本书上看到关于回调函数的定义.我在百度上搜了一下,发现众说纷纭,有很大一部分都是使用类 ...

  5. log4net内部调试开启

    最近用log4net写入日志到mysql数据库,死活写不进去,就想能不能看log4net的错误记录,在网上一找,还真有:开启log4net的内部调试,作个记录: <appSettings> ...

  6. 【转】在Linux下使用Jmeter执行测试任务

    想在Linux下运行jmeter必须先安装jdk,安装步骤如下: (1).下载一个linux可用的jdk包(比如:jdk-6u45-linux-i586.bin), (2).然后将jdk-6u45-l ...

  7. xunsearch基本使用

    目录 基本实践 异常捕获 XSDocument 文档 添加操作 ini 更新.修改文档 删除文档 清空索引 平滑重建索引 使用索引缓冲区 自定义 SCWS 词库 基本实践 <?php // 引入 ...

  8. platform驱动之probe函数

    驱动注册的probe函数 probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工 ...

  9. Android Eclipse调试技巧

    在Android 应用程序开发中我们经常需要调试程序,可以说调试在我们的日常开发中起着十分重要的作用,下面就以Elipse开发环境总结一下调试技巧. 一.Debug 断点调试 所谓断点调试就是指在程序 ...

  10. JS||JQUERY常用语法

    cookieEnabled属性语法 通常可以在浏览器的临时文件夹中保存一个文件,此文件可以包含用户信息(比如浏览过什么页面,是否选择了自动登录)等,这个文件被称作cookie,通过cookieEnab ...