将Json String解析为Java对象
相对于“将Java 对象转换Json String”更为复杂一些,这里分两步走:
第一步,将Json String 转换为一个Java JsonObject类,JsonObject类似一个树结构,用于存储Json信息。
第二步,将Java JsonObject类解析为Java对象实例。
将Json String 转换为一个Java JsonObject类:
JsonObject实际就是对应Json结构,val->Json Value(val只能是Java String,Java Map,Java Lis,其中Map->Json Object,List->Json Array),key->Json key三种结构,由于是嵌套的,所以还定义了一个parent,具体见Json#JsonObject.java:
static class JsonObject {
private JsonObject parent;
private String key;
private Object val;
public JsonObject(JsonObject parent, String key, Map<String, Object> val) {
this.parent = parent;
this.key = key;
this.val = val;
relateParent(parent, key, val);
}
public JsonObject(JsonObject parent, String key, List<Object> val) {
this.parent = parent;
this.key = key;
this.val = val;
relateParent(parent, key, val);
}
/**
* 与父类建立关系
*/
@SuppressWarnings("unchecked")
private void relateParent(JsonObject parent, String key, Object val) {
if (parent != null && val != null) {
Object pVal = parent.getVal();
if (pVal != null) {
if (pVal instanceof Map && CommonUtils.isNotEmpty(key)) {
((Map<String, Object>) pVal).put(key, val);
} else if (pVal instanceof List) {
((List<Object>) pVal).add(val);
}
}
}
}
public JsonObject getParent() {
if (parent == null) {
parent = this;
}
return parent;
}
public String getKey() {
return key;
}
public Object getVal() {
return val;
}
@Override
public String toString() {
return val != null ? val.toString() : null;
}
}
将Json String解析为JsonObject流程图:
最终解析出来的是一个JsonObject对象实例,该对象实例中又包含了子JsonObject,就像一个树结构,具体代码如下:
private static JsonObject toJsonObject(String jsonStr) {
if (CommonUtils.isEmpty(jsonStr)) {
return null;
}
JsonObject jb = null;
StringBuilder sbd = new StringBuilder();
char[] jcs = jsonStr.toCharArray();
int jLen = jcs.length;
String key = null;
String val = null;
Object jVal = null;
char jc;
for (int index = 0; index < jLen; index++) {
jc = jcs[index];
if (jc == '{') {
// 初始化为Map
jb = new JsonObject(jb, key, new HashMap<String, Object>());
} else if (jc == '[') {
// 初始化为List
jb = new JsonObject(jb, key, new ArrayList<Object>());
} else if (jc == ':') {
// 获取key
key = getAndCleanSbdStr(sbd);
} else if (jc == ',' || jc == '}' || jc == ']') {
// 获取val
val = getAndCleanSbdStr(sbd);
// 处理值
if (jb != null && CommonUtils.isNotEmpty(val)) {
jVal = jb.getVal();
if (jVal instanceof Map && CommonUtils.isNotEmpty(key)) {
((Map<String, Object>) jVal).put(key, val);
} else if (jVal instanceof List) {
((List<Object>) jVal).add(val);
}
}
// 当前对象处理介绍,回退到上一个对象
if (jc == '}' || jc == ']') {
jb = jb.getParent();
key = jb.getKey();
}
} else {
sbd.append(jc);
}
}
return jb;
}
将Java JsonObject类解析为Java对象实例
这里会有两个参数,一个是Java JsonObject实例值,一个是目标Java对象class,转换后的结果是目标Java对象实例。如《Java实现一个简单的Json解析器(一)》提到的本文的Java对象类型分类,分别对目标Java对象的类型进行判断,根据不同类型,分别做不同的类型解析处理,解析流程如下:
有几个地方需要重点说明一下:
1、整个解析过程是嵌套执行,目标Java对象的类型和JsonObject对象的类型能对应上才能解析,否则可能被丢弃,对应关系大体如下:
(1) 目标Java对象为简单类型,则JsonObject对象的值可以为任意类型,但解析是强制转换为String再进行解析。
(2) 目标Java对象为复合或者Map类型,则JsonObject对象的值只能为Map类型。
(3) 目标Java对象为集合或者数组类型,则JsonObject对象的值只能为集合类型。
2、对于目标Java对象为非简单类型时,需要判断出pojo类中的字段,或者Map,数组,集合中定义的值对应哪个Java类(比如byte[String],则定义的值为String.class类,又比如Map<String,TestDto>,则定义的值为TestDto.class类,若无法确定,则取默认的Java的Object.class),以便作为新的目标Java对象进行嵌套解析,最终最底层都是调用简单类型进行处理,具体代码见如下:
private static Class<?> getComponentType(Type gType) {
CommonUtils.debugLog("type is " + gType);
if (gType instanceof Class) {
Class<?> clazz = (Class<?>) gType;
if (TypeUtils.isArray(clazz)) {
return clazz.getComponentType();
}
}
if (gType != null) {
if (gType instanceof ParameterizedType) {
Type[] ctgs = ((ParameterizedType) gType).getActualTypeArguments();
if (ctgs.length > 0) {
// collection,map等,只取最后一个参数
return (Class<?>) ctgs[ctgs.length - 1];
}
} else if (gType instanceof WildcardType) {
return (Class<?>) ((WildcardType) gType).getUpperBounds()[0];
} else if (gType instanceof GenericArrayType) {
return (Class<?>) ((GenericArrayType) gType).getGenericComponentType();
} else if (gType instanceof TypeVariable) {
return (Class<?>) ((TypeVariable) gType).getBounds()[0];
}
}
return Object.class;
}
1、对于目标Java对象中有些字段使用接口进行定义,若无法找到具体的实现类时,则通过Java代理实现setter和getter方法,代码实现如下:
static class JsonInvocationHandler implements InvocationHandler {
private Map srcVals = null;
private Map<String, Object> vals = new HashMap<String, Object>();
public JsonInvocationHandler(Map srcVals) {
this.srcVals = srcVals;
}
@Override
public int hashCode() {
int code = 1;
for (Object obj : vals.values()) {
if (obj != null) {
code = code * 31 + obj.hashCode();
}
}
return code;
}
@Override
public Object invoke(Object ins, Method m, Object[] valObjs) throws Throwable {
// 重点代理实现getter和setter方法
try {
String mName = m.getName();
Type[] pts = m.getGenericParameterTypes();
Class<?> retType = m.getReturnType();
if (pts.length == 1 && retType.equals(void.class)) {
if (mName.startsWith("set")) {
String fieldName = MethodUtils.getFieldName(m, "set");
vals.put(fieldName, valObjs[0]);
}
} else if (pts.length == 0 && !retType.equals(void.class)) {
if (mName.startsWith("get")) {
String fieldName = MethodUtils.getFieldName(m, "get");
if (fieldName != null) {
if (!vals.containsKey(fieldName)) {
return convertVal(pts, retType, fieldName);
} else {
return vals.get(fieldName);
}
}
} else if (mName.startsWith("is")) {
String fieldName = MethodUtils.getFieldName(m, "is");
if (fieldName != null) {
if (!vals.containsKey(fieldName)) {
return convertVal(pts, retType, fieldName);
} else {
return vals.get(fieldName);
}
}
} else if (mName.startsWith("toString")) {
Set<String> keySet = srcVals.keySet();
for (String fileName : keySet) {
if (!vals.containsKey(fileName)) {
Object srcVal = srcVals.get(fileName);
if (srcVal != null) {
Method getM = null;
String upperField = fileName.substring(0, 1).toUpperCase() + fileName.substring(1);
try {
getM = srcVal.getClass().getMethod("get" + upperField, null);
} catch (Exception e) {
e.printStackTrace();
try {
getM = srcVal.getClass().getMethod("is" + upperField, null);
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (getM != null) {
convertVal(null, getM.getReturnType(), fileName);
}
}
}
}
return vals.toString();
} else if (mName.startsWith("hashCode")) {
return this.hashCode();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Object convertVal(Type[] pts, Class<?> retType, String fieldName) {
// 值获取不到时,初始化转换
Object retSrcVal = srcVals.get(fieldName);
if (retSrcVal != null) {
// 做值转换
Class<?> cType = getComponentType((pts != null && pts.length > 0) ? pts[0] : retType);
Object retVal = parseObjectInner(retType, retSrcVal, cType);
if (retVal != null) {
vals.put(fieldName, retVal);
}
return retVal;
}
return null;
}
}
可优化地方:
1、日期(格式)处理
2、数值的处理,如科学计数法转换等
3、字符串编码处理,避免出现解析或者转换的乱码
4、格式化输出处理
5、性能和容错处理
总结:
1、理解Json数据结构基础上,通过与Java语言特性进行对应,实现Json解析器功能
2、划分类型,针对不同类型进行不同的处理,类型最终处理都归结为简单类型的处理
3、针对复合类型处理,需化繁为简进行处理
4、针对集合或者Map类型处理,首先要能够提取集合中对象的类型或者值,然后进行循环处理
5、理解代理实现类