Last active
January 12, 2018 09:35
-
-
Save pfmiles/0c9abadfd82f9783f211 to your computer and use it in GitHub Desktop.
完整保留类型信息的json序列化/反序列化工具
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package test; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Queue; | |
import java.util.Set; | |
import org.junit.Before; | |
import org.junit.Test; | |
import com.alibaba.fastjson.JSON; | |
/** | |
* @author pf-miles Feb 4, 2016 3:20:59 PM | |
*/ | |
public class RuleSerializationUtilTest { | |
public static final class Apple { | |
private Apple inner; | |
public Apple getInner() { | |
return inner; | |
} | |
public void setInner(Apple inner) { | |
this.inner = inner; | |
} | |
@Override | |
public String toString() { | |
return "Apple [inner=" + inner + "]"; | |
} | |
} | |
public static final class Box { | |
private Apple apple; | |
private List<Apple> apples; | |
private Apple[] appleArr; | |
private Map<String, String> map; | |
private Set<Apple> appleSet; | |
private Queue<Apple> appleQueue; | |
public Queue<Apple> getAppleQueue() { | |
return appleQueue; | |
} | |
public void setAppleQueue(Queue<Apple> appleQueue) { | |
this.appleQueue = appleQueue; | |
} | |
public Apple getApple() { | |
return apple; | |
} | |
public void setApple(Apple apple) { | |
this.apple = apple; | |
} | |
public List<Apple> getApples() { | |
return apples; | |
} | |
public void setApples(List<Apple> apples) { | |
this.apples = apples; | |
} | |
public Apple[] getAppleArr() { | |
return appleArr; | |
} | |
public void setAppleArr(Apple[] appleArr) { | |
this.appleArr = appleArr; | |
} | |
public Map<String, String> getMap() { | |
return map; | |
} | |
public void setMap(Map<String, String> map) { | |
this.map = map; | |
} | |
public Set<Apple> getAppleSet() { | |
return appleSet; | |
} | |
public void setAppleSet(Set<Apple> appleSet) { | |
this.appleSet = appleSet; | |
} | |
@Override | |
public String toString() { | |
return "Box [apple=" + apple + ", apples=" + apples + ", appleArr=" | |
+ Arrays.toString(appleArr) + ", map=" + map + ", appleSet=" + appleSet | |
+ ", appleQueue=" + appleQueue + "]"; | |
} | |
} | |
// 单个object | |
private Box box; | |
// object list | |
private List<Box> boxes; | |
@Before | |
public void init() { | |
Apple a = new Apple(); | |
this.box = new Box(); | |
List<Apple> alist = new ArrayList<Apple>(); | |
alist.add(a); | |
Apple[] aArr = new Apple[1]; | |
aArr[0] = a; | |
Map<String, String> map = new HashMap<String, String>(); | |
map.put("test", "test"); | |
Set<Apple> aSet = new HashSet<Apple>(); | |
aSet.add(a); | |
Queue<Apple> aQueue = new LinkedList<Apple>(); | |
aQueue.add(a); | |
this.box.setApple(a); | |
this.box.setAppleArr(aArr); | |
this.box.setApples(alist); | |
this.box.setAppleSet(aSet); | |
this.box.setMap(map); | |
this.box.setAppleQueue(aQueue); | |
this.boxes = new ArrayList<Box>(); | |
this.boxes.add(box); | |
} | |
@Test | |
public void testToJavaObject() { | |
// obj | |
String jsonStr = JSON.toJSONString(this.box); | |
Box b = RuleSerializationUtil.toJavaObject(jsonStr, Box.class); | |
System.out.println(b); | |
// list | |
jsonStr = JSON.toJSONString(this.boxes); | |
List<Box> bs = RuleSerializationUtil.toJavaList(jsonStr, Box.class); | |
System.out.println(bs); | |
// list用toJavaObject, 虽然能成功,但无法做强类型转换 | |
jsonStr = JSON.toJSONString(this.boxes); | |
bs = RuleSerializationUtil.toJavaObject(jsonStr, List.class); | |
System.out.println(bs); | |
} | |
// 测试to json string | |
@Test | |
public void testToJsonString() { | |
System.out.println(RuleSerializationUtil.toJsonString(this.box)); | |
System.out.println(RuleSerializationUtil.toJsonString(this.boxes)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package test; | |
import java.beans.BeanInfo; | |
import java.beans.Introspector; | |
import java.beans.PropertyDescriptor; | |
import java.lang.reflect.Array; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.lang.reflect.ParameterizedType; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.ListIterator; | |
import java.util.Map; | |
import java.util.Queue; | |
import java.util.Set; | |
import org.slf4j.Logger; | |
import com.alibaba.fastjson.JSON; | |
import com.alibaba.fastjson.JSONArray; | |
import com.alibaba.fastjson.JSONObject; | |
public abstract class TypeSafeSerializationUtil { | |
/** | |
* json对象类型标记 | |
*/ | |
public static final String CLAZZ = "_clazz"; | |
//老数据兼容 | |
private static final String CLASS = "class"; | |
private static final Set<Class<?>> primitiveClasses = new HashSet<Class<?>>(); | |
static { | |
primitiveClasses.add(boolean.class); | |
primitiveClasses.add(Boolean.class); | |
primitiveClasses.add(char.class); | |
primitiveClasses.add(Character.class); | |
primitiveClasses.add(byte.class); | |
primitiveClasses.add(Byte.class); | |
primitiveClasses.add(short.class); | |
primitiveClasses.add(Short.class); | |
primitiveClasses.add(int.class); | |
primitiveClasses.add(Integer.class); | |
primitiveClasses.add(long.class); | |
primitiveClasses.add(Long.class); | |
primitiveClasses.add(float.class); | |
primitiveClasses.add(Float.class); | |
primitiveClasses.add(double.class); | |
primitiveClasses.add(Double.class); | |
primitiveClasses.add(BigInteger.class); | |
primitiveClasses.add(BigDecimal.class); | |
primitiveClasses.add(String.class); | |
primitiveClasses.add(java.util.Date.class); | |
primitiveClasses.add(java.sql.Date.class); | |
primitiveClasses.add(java.sql.Time.class); | |
primitiveClasses.add(java.sql.Timestamp.class); | |
} | |
private static final Logger LOGGER = org.slf4j.LoggerFactory | |
.getLogger(TypeSafeSerializationUtil.class); | |
/** | |
* 将java对象序列化为json字符串, 带类型信息 | |
*/ | |
public static String toJsonString(Object javaObj) { | |
if (javaObj == null) | |
return null; | |
// 先将传入参数转换为带“class”key的map或List | |
Object toFastJson = toClassKnowingMapOrArray(javaObj); | |
// 再由fastJson输出 | |
return JSON.toJSONString(toFastJson); | |
} | |
/** | |
* 将对象、list转换为list/map结构树, 带类型信息 | |
*/ | |
public static Object toMapOrList(Object javaObj) { | |
if (javaObj == null) | |
return null; | |
// 先将传入参数转换为带“class”key的map或List | |
return toClassKnowingMapOrArray(javaObj); | |
} | |
@SuppressWarnings("unchecked") | |
private static Object toClassKnowingMapOrArray(Object javaObj) { | |
if (javaObj == null) | |
return null; | |
Class<?> cls = javaObj.getClass(); | |
if (cls.isArray()) { | |
List<Object> ret = new ArrayList<Object>(); | |
int l = Array.getLength(javaObj); | |
for (int i = 0; i < l; i++) { | |
ret.add(toClassKnowingMapOrArray(Array.get(javaObj, i))); | |
} | |
return ret; | |
} else if (Collection.class.isAssignableFrom(cls)) { | |
List<Object> ret = new ArrayList<Object>(); | |
for (Object o : ((Collection<?>) javaObj)) { | |
ret.add(toClassKnowingMapOrArray(o)); | |
} | |
return ret; | |
} else if (Map.class.isAssignableFrom(cls)) { | |
for (Map.Entry<Object, Object> e : ((Map<Object, Object>) javaObj).entrySet()) { | |
e.setValue(toClassKnowingMapOrArray(e.getValue())); | |
} | |
return javaObj; | |
} else if (Enum.class.isAssignableFrom(cls)) { | |
return javaObj; | |
} else if (Class.class.equals(cls)) { | |
return javaObj; | |
} else if (isPrimitive(cls)) { | |
return javaObj; | |
} else if (cls.isAnnotation()) { | |
return javaObj; | |
} else {// 'normal' object, treated as the java-bean spec. goes. | |
Map<String, Object> ret = new HashMap<String, Object>(); | |
try { | |
BeanInfo bi = Introspector.getBeanInfo(cls); | |
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { | |
if (pd.getReadMethod() != null) { | |
if (CLASS.equals(pd.getName())) { | |
ret.put(CLAZZ, | |
((Class<?>) pd.getReadMethod().invoke(javaObj)).getName()); | |
} else { | |
ret.put(pd.getName(), | |
toClassKnowingMapOrArray(pd.getReadMethod().invoke(javaObj))); | |
} | |
} | |
} | |
} catch (Throwable e) { | |
LOGGER.error("", e); | |
} | |
return ret; | |
} | |
} | |
private static boolean isPrimitive(Class<?> cls) { | |
return primitiveClasses.contains(cls); | |
} | |
/** | |
* json字符串解析并映射到指定类型的对象类型上 | |
* @param jsonStr 要解析的json字符串 | |
* @param retCls 期望返回的对象类型 | |
* @return 指定类型的映射好的对象 | |
*/ | |
public static <T> T toJavaObject(String jsonStr, Class<T> retCls) { | |
return toJavaObject(jsonStr, retCls, null); | |
} | |
/** | |
* json字符串解析并映射到指定类型的对象类型上 | |
* @param jsonStr 要解析的json字符串 | |
* @param retCls 期望返回的对象类型 | |
* @param cl 反序列化对象时期望使用的classloader | |
* @return 指定类型的映射好的对象 | |
*/ | |
public static <T> T toJavaObject(String jsonStr, Class<T> retCls, ClassLoader cl) { | |
if (jsonStr == null) | |
return null; | |
// 先由JSON转为JSONObject/JSONArray | |
Object obj = JSON.parse(jsonStr); | |
// 再由bean mapping映射为类 | |
return toJavaObject(obj, retCls, cl); | |
} | |
// 将list以反射形式转换成array | |
private static <T> Object listToArray(Class<?> arrayComponentType, List<?> list) { | |
Object arr = Array.newInstance(arrayComponentType, list.size()); | |
for (int i = 0; i < list.size(); i++) | |
Array.set(arr, i, list.get(i)); | |
return arr; | |
} | |
private static Object typeMappingFromJsonObj(Object jsonObj, ClassLoader cl) { | |
if (jsonObj == null) | |
return null; | |
Class<?> cls = jsonObj.getClass(); | |
Object ret = null; | |
if (JSONArray.class.equals(cls)) {// collection或array | |
List<Object> list = new ArrayList<Object>(); | |
JSONArray arr = (JSONArray) jsonObj; | |
for (int i = 0; i < arr.size(); i++) | |
list.add(typeMappingFromJsonObj(arr.get(i), cl)); | |
ret = list; | |
} else if (JSONObject.class.equals(cls)) {// map或pojo | |
JSONObject m = (JSONObject) jsonObj; | |
if (m.containsKey(CLAZZ)) { | |
Object clsStr = m.get(CLAZZ); | |
try { | |
// pojo | |
Class<?> pojoCls = null; | |
if (cl != null) { | |
pojoCls = cl.loadClass(clsStr.toString()); | |
} else { | |
pojoCls = Class.forName(clsStr.toString()); | |
} | |
ret = pojoCls.newInstance(); | |
// mapping properties, reflectively | |
BeanInfo bi = Introspector.getBeanInfo(pojoCls); | |
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { | |
if (CLASS.equals(pd.getName())) | |
continue; | |
if (pd.getWriteMethod() != null && m.containsKey(pd.getName())) { | |
Object jsonVal = m.get(pd.getName()); | |
Class<?> propertyType = pd.getPropertyType(); | |
if (jsonVal instanceof JSONObject) { | |
// 若是json map, 且目标侧的属性类型不是map或primitive,且数据本身不带clazz,则尝试辅助刷上类型 | |
JSONObject jo = ((JSONObject) jsonVal); | |
if (!jo.containsKey(CLAZZ) | |
&& !Map.class.isAssignableFrom(propertyType) | |
&& !isPrimitive(propertyType)) { | |
jo.put(CLAZZ, propertyType.getName()); | |
} | |
} else if (jsonVal instanceof JSONArray) { | |
// 若是json array,且目标侧的属性类型是带明确泛型实参的list/set或类型明确的pojo数组, 且不是primitive或map, 且数据本身不带clazz,则尝试辅助刷上类型 | |
JSONArray ja = (JSONArray) jsonVal; | |
if (Collection.class.isAssignableFrom(propertyType) | |
|| propertyType.isArray()) { | |
Class<?> eleType = getEleType(propertyType, | |
pd.getWriteMethod()); | |
if (eleType != null && !Map.class.isAssignableFrom(eleType) | |
&& !isPrimitive(eleType)) { | |
ListIterator<Object> os = ja.listIterator(); | |
while (os.hasNext()) { | |
Object o = os.next(); | |
if (o instanceof JSONObject | |
&& !((JSONObject) o).containsKey(CLAZZ)) { | |
((JSONObject) o).put(CLAZZ, eleType.getName()); | |
} else if (o instanceof String && eleType != null) { | |
os.set(resolveEnumOrCls((String) o, eleType, cl)); | |
} | |
} | |
} | |
} | |
} else if (jsonVal instanceof String && propertyType != null) { | |
// 处理enum和class属性的情况 | |
jsonVal = resolveEnumOrCls((String) jsonVal, propertyType, cl); | |
} | |
Object val = typeMappingFromJsonObj(jsonVal, cl); | |
val = arrSetQueCompatible(val, propertyType); | |
try { | |
pd.getWriteMethod().invoke(ret, val); | |
} catch (IllegalArgumentException e) { | |
// ignore... | |
} | |
} | |
} | |
} catch (Throwable e) { | |
LOGGER.error("jsonObject " + jsonObj, e); | |
} | |
} else {// map | |
Map<String, Object> map = new HashMap<String, Object>(); | |
JSONObject jo = (JSONObject) jsonObj; | |
for (Map.Entry<String, Object> e : jo.entrySet()) { | |
map.put(e.getKey(), typeMappingFromJsonObj(e.getValue(), cl)); | |
} | |
ret = map; | |
} | |
} else if (Enum.class.isAssignableFrom(cls)) {// enum | |
ret = jsonObj; | |
} else if (Class.class.equals(cls)) {// class | |
ret = jsonObj; | |
} else if (cls.isAnnotation()) {// annotation | |
ret = jsonObj; | |
} else { // primitive | |
ret = jsonObj; | |
} | |
return ret; | |
} | |
@SuppressWarnings({ "unchecked", "rawtypes" }) | |
private static Object resolveEnumOrCls(String str, Class<?> cls, | |
ClassLoader cl) throws ClassNotFoundException { | |
if (Enum.class.isAssignableFrom(cls)) { | |
return Enum.valueOf((Class<Enum>) cls, str); | |
} else if (Class.class.equals(cls)) { | |
if (cl != null) { | |
return cl.loadClass(str); | |
} else { | |
return Class.forName(str); | |
} | |
} | |
return str; | |
} | |
// 尝试将val转换为指定的类型, 用于支持反序列化目标类型为array, set, queue的场景 | |
private static Object arrSetQueCompatible(Object val, Class<?> type) { | |
// 兼容array | |
if (type.isArray() && val instanceof List) { | |
val = listToArray(type.getComponentType(), (List<?>) val); | |
} | |
// 兼容set/queue场景 | |
if (Set.class.isAssignableFrom(type) && val instanceof Collection) { | |
val = new HashSet<Object>((Collection<?>) val); | |
} else if (Queue.class.isAssignableFrom(type) && val instanceof Collection) { | |
val = new LinkedList<Object>((Collection<?>) val); | |
} | |
return val; | |
} | |
// 获取list或array类型的元素类型 | |
private static Class<?> getEleType(Class<?> col, Method writer) { | |
if (col.isArray()) { | |
return col.getComponentType(); | |
} else if (Collection.class.isAssignableFrom(col)) { | |
// 尝试从writer method身上获取参数的泛型实参类型 | |
try { | |
return (Class<?>) ((ParameterizedType) writer.getGenericParameterTypes()[0]) | |
.getActualTypeArguments()[0]; | |
} catch (Exception e) { | |
// ignore... | |
return null; | |
} | |
} else { | |
return null; | |
} | |
} | |
/** | |
* 将parse好的fast json对象映射到指定类型的对象类型上 | |
* @param json parse好的fast json对象 | |
* @param retCls 期望返回的目标对象类型 | |
* @return 指定类型的对象实例 | |
*/ | |
public static <T> T toJavaObject(Object json, Class<?> retCls) { | |
return toJavaObject(json, retCls, null); | |
} | |
/** | |
* 将parse好的fast json对象映射到指定类型的对象类型上 | |
* @param json parse好的fast json对象 | |
* @param retCls 期望返回的目标对象类型 | |
* @param cl 反序列化类时期望使用的classloader | |
* @return 指定类型的对象实例 | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T toJavaObject(Object json, Class<?> retCls, ClassLoader cl) { | |
// 若指定cls不是array或collection或map或primitive或interface或abstract cls, 且数据本身不带clazz, 则尝试主动为json刷上类型 | |
if (retCls != null && !retCls.isArray() && !Collection.class.isAssignableFrom(retCls) | |
&& !Map.class.isAssignableFrom(retCls) && !isPrimitive(retCls) | |
&& !Class.class.equals(retCls) && !Enum.class.isAssignableFrom(retCls) | |
&& !retCls.isAnnotation() && !retCls.isInterface() | |
&& !Modifier.isAbstract(retCls.getModifiers()) && json instanceof JSONObject | |
&& !((JSONObject) json).containsKey(CLAZZ)) { | |
((JSONObject) json).put(CLAZZ, retCls.getName()); | |
} | |
// 由bean mapping映射为类 | |
Object ret = typeMappingFromJsonObj(json, cl); | |
// 支持一下retCls为array, set, queue时的场景 | |
ret = arrSetQueCompatible(ret, retCls); | |
return (T) ret; | |
} | |
/** | |
* 将jsonArray格式的字符串反序列化为List | |
* @param jsonStr | |
* @param eleType list的元素类型 | |
*/ | |
public static <T> List<T> toJavaList(String jsonStr, Class<T> eleType) { | |
if (jsonStr == null) | |
return null; | |
JSONArray arr = JSON.parseArray(jsonStr); | |
return toJavaList(arr, eleType); | |
} | |
/** | |
* 将指定的fast json array转换为指定元素类型的list | |
* @param jsonArr 指定的fast json array | |
* @param eleType 期望得到的list的元素类型 | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> List<T> toJavaList(JSONArray jsonArr, Class<T> eleType) { | |
if (jsonArr == null) | |
return null; | |
if (jsonArr.isEmpty()) | |
return Collections.emptyList(); | |
List<Object> ret = new ArrayList<Object>(); | |
for (Object ele : jsonArr) { | |
T object = toJavaObject(ele, eleType); | |
if (object != null) { | |
ret.add(object); | |
} | |
} | |
return (List<T>) ret; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment