项目作者: slive

项目描述 :
Java实现一个简单的Json解析器
高级语言: Java
项目地址: git://github.com/slive/simpleJson.git
创建时间: 2020-12-08T15:37:19Z
项目社区:https://github.com/slive/simpleJson

开源协议:

下载


Java实现一个简单的Json解析器

基于Json进行数据传输在现在的互联网应用中已经非常非常广泛,所以了解Json数据结构是很有必要的。为了加深理解,自己动手实现一个简单的Json解析器。
实现Json解析器包括两方面:
a、将Java 对象转换为Json String
b、将Json String解析为Java对象

简单了解Json数据结构

Json主要由如下两种结构和值组成:
Json Object,即为键值对方式,键只能为字符串类型,值则如常规字符串,数字,日期,对象,hash表,数组,集合等等。由“{”开头,“}”结尾,内由多组键-值组成,键-值中间用“:”隔开,键值与键值之前用“,”隔开,具体如下:

<img src=”http://www.json.org/object.gif“</img

Json Array,由“[”开头,“]”结尾,内由多个值组成,每个值之间用“,”隔开,具体如下:

<img src=”http://www.json.org/array.gif“</img

Json Value,可以是字符串,数字,布尔,或者嵌套Json Object,Array,也可能为空,具体如下:

<img src=”http://www.json.org/value.gif“</img

Java对象类型分类

对于Java对象的类型,本文及其后面文章约定了几种类型的定义(可能定义有一定的局限性和不完整):

  1. 1、简单类型
    a)Java8种基本类型及其包装类型
    b)Java String类型
    c)继承Java Number类的类型
    2Collection类型
    a)实现Java Collection接口的类型
    b)继承Java Collection 接口的类型
    3Map类型
    a)实现Java Map接口类型
    b)继承Java Map接口的类型
    4、数组类型
    Java 中定义的数组类型
    5、复合类型
    以上类型除外

代码见TypeUtils.java:



  1. /*

  1. * booleanbytecharshortintlongfloat double
  2. * @see Class.isPrimitive()
  3. */
  4. public static boolean isPrimitiveType(Class<? clazz) {
  5. return clazz != null ? clazz.isPrimitive() : false;
  6. }
  7. public static boolean isPrimitiveWrapperType(Class<? clazz) {
  8. if (clazz != null) {
  9. // 抽象类 Number 是 BigDecimal、BigInteger、Byte、Double、Float、Integer、Long 和 Short 类的超类
  10. if (Number.class.isAssignableFrom(clazz)) {
  11. return true;
  12. } else if (Boolean.class.equals(clazz)) {
  13. return true;
  14. }
  15. }
  16. return false;
  17. }
  18. public static boolean isStringType(Class<? clazz) {
  19. return String.class.equals(clazz);
  20. }
  21. public static boolean isArray(Class<? clazz) {
  22. return clazz != null ? clazz.isArray() : false;
  23. }
  24. public static boolean isMap(Class<? clazz) {
  25. return isContainType(clazz, Map.class);
  26. }
  27. public static boolean isSet(Class<? clazz) {
  28. return isContainType(clazz, Set.class);
  29. }
  30. public static boolean isList(Class<? clazz) {
  31. return isContainType(clazz, List.class);
  32. }
  33. public static boolean isCollection(Class<? clazz) {
  34. return isContainType(clazz, Collection.class);
  35. }
  36. private static boolean isContainType(Class<? clazz, Class<? cClazz) {
  37. return cClazz.isAssignableFrom(clazz);
  38. }
  39. /**
  40. * 简单类型:原始类型,原始类型的包装类及String类型都归类为
  41. */
  42. public static boolean isSimpleType(Class<? clazz) {
  43. return isStringType(clazz) || isPrimitiveWrapperType(clazz) || isPrimitiveType(clazz);
  44. }
  45. /**
  46. * 复合类型:简单类型,集合,数组,Map除外
  47. */
  48. public static boolean isComplexType(Class<? clazz) {
  49. return !(isSimpleType(clazz) || isMap(clazz) || isList(clazz) || isSet(clazz) || isArray(clazz));
  50. }

</code
</pre

将Java 对象转换为Json String

Java 对象转Json String大体流程如下:
<img src=”JsonString.png”</img

  1. 1、对于输入的Java对象实例,首先判断是属于那种类型,然后根据不同类型分别进行处理,处理简单类型,其它都是需要进行嵌套处理,对于Map,集合,数组类型,还需要循环嵌套处理,最终底层来看都是根据简单类型进行处理,然后将处理结果统一到一个StringBuilder中返回。
  2. 2、简单类型处理,对应Json Value,直接将简单类型转换为String类型,然后返回。
  3. 3、复合类型处理,对应Json Object,这里只能处理pojo类,其它不符合该要求的,将无法解析。通过判断对象实例的gettersetter获取对应的字段名称,也即为key,然后通过getter方法获取值,再对值回到1中嵌套解析处理。
  4. 4Map类型处理,也是对应Json Object,这里的Map中的key对应着Json key,通过循环迭代获取值,再对值回到1中嵌套解析处理。
  5. 5、数组/集合类型处理,也是对应Json Array,通过循环迭代获取值,再对值回到1中嵌套解析处理。
  6. 最后再将结果返回即可。
  7. 代码参考Json.java
  1. /* 将类转换为json格式字符串
  2. */
  3. public static String toJsonString(Object jsonObj) {
  4. if (jsonObj == null) {
  5. return null;
  6. }
  7. ClassJsonObject实际就是对应Json结构,val-Json Valueval只能是Java StringJava MapJava Lis,其中Map-Json ObjectList-Json Array),key- Json key三种结构,由于是嵌套的,所以还定义了一个parent,具体见Json#JsonObject.java:
  8. Json String解析为JsonObject流程图:
  9. 最终解析出来的是一个JsonObject对象实例,该对象实例中又包含了子JsonObject,就像一个树结构,具体代码如下:
  10. private static JsonObject toJsonObject(String jsonStr) {
  11.         if (CommonUtils.isEmpty(jsonStr)) {
  12.             return null;
  13.         }
  14.         JsonObject jb = null;
  15.         StringBuilder sbd = new StringBuilder();
  16.         char[] jcs = jsonStr.toCharArray();
  17.         int jLen = jcs.length;
  18.         String key = null;
  19.         String val = null;
  20.         Object jVal = null;
  21.         char jc;
  22.         for (int index = 0; index < jLen; index++) {
  23.             jc = jcs[index];
  24.             if (jc == '{') {
  25.                 // 初始化为Map
  26.                 jb = new JsonObject(jb, key, new HashMap
  27. Java JsonObject类解析为Java对象实例

  28. 这里会有两个参数,一个是Java JsonObject实例值,一个是目标Java对象class,转换后的结果是目标Java对象实例。如《Java实现一个简单的Json解析器(一)》提到的本文的Java对象类型分类,分别对目标Java对象的类型进行判断,根据不同类型,分别做不同的类型解析处理,解析流程如下:
    <img src=”JavaParse.png”</img

  29. 有几个地方需要重点说明一下:
    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类,若无法确定,则取默认的JavaObject.class),以便作为新的目标Java对象进行嵌套解析,最终最底层都是调用简单类型进行处理,具体代码见如下:

  30. <pre
    <code
    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;
    }
    </code
    </pre

  31. 3、对于目标Java对象中有些字段使用接口进行定义,若无法找到具体的实现类时,则通过Java代理实现settergetter方法,代码实现如下:


  32. static class JsonInvocationHandler implements InvocationHandler {

  33.     private Map srcVals = null;
  34.     private Map<String, Object vals = new HashMap<String, Object();
  35.     public JsonInvocationHandler(Map srcVals) {
  36.         this.srcVals = srcVals;
  37.     }
  38.     @Override
  39.     public int hashCode() {
  40.         int code = 1;
  41.         for (Object obj : vals.values()) {
  42.             if (obj != null) {
  43.                 code = code * 31 + obj.hashCode();
  44.             }
  45.         }
  46.         return code;
  47.     }
  48.     @Override
  49.     public Object invoke(Object ins, Method m, Object[] valObjs) throws Throwable {
  50.         // 重点代理实现getter和setter方法
  51.         try {
  52.             String mName = m.getName();
  53.             Type[] pts = m.getGenericParameterTypes();
  54.             Class<? retType = m.getReturnType();
  55.             if (pts.length == 1 && retType.equals(void.class)) {
  56.                 if (mName.startsWith("set")) {
  57.                     String fieldName = MethodUtils.getFieldName(m, "set");
  58.                     vals.put(fieldName, valObjs[0]);
  59.                 }
  60.             } else if (pts.length == 0 && !retType.equals(void.class)) {
  61.                 if (mName.startsWith("get")) {
  62.                     String fieldName = MethodUtils.getFieldName(m, "get");
  63.                     if (fieldName != null) {
  64.                         if (!vals.containsKey(fieldName)) {
  65.                             return convertVal(pts, retType, fieldName);
  66.                         } else {
  67.                             return vals.get(fieldName);
  68.                         }
  69.                     }
  70.                 } else if (mName.startsWith("is")) {
  71.                     String fieldName = MethodUtils.getFieldName(m, "is");
  72.                     if (fieldName != null) {
  73.                         if (!vals.containsKey(fieldName)) {
  74.                             return convertVal(pts, retType, fieldName);
  75.                         } else {
  76.                             return vals.get(fieldName);
  77.                         }
  78.                     }
  79.                 } else if (mName.startsWith("toString")) {
  80.                     Set<String keySet = srcVals.keySet();
  81.                     for (String fileName : keySet) {
  82.                         if (!vals.containsKey(fileName)) {
  83.                             Object srcVal = srcVals.get(fileName);
  84.                             if (srcVal != null) {
  85.                                 Method getM = null;
  86.                                 String upperField = fileName.substring(0, 1).toUpperCase() + fileName.substring(1);
  87.                                 try {
  88.                                     getM = srcVal.getClass().getMethod("get" + upperField, null);
  89.                                 } catch (Exception e) {
  90.                                     e.printStackTrace();
  91.                                     try {
  92.                                         getM = srcVal.getClass().getMethod("is" + upperField, null);
  93.                                     } catch (Exception e2) {
  94.                                         e2.printStackTrace();
  95.                                     }
  96.                                 }
  97.                                 if (getM != null) {
  98.                                     convertVal(null, getM.getReturnType(), fileName);
  99.                                 }
  100.                             }
  101.                         }
  102.                     }
  103.                     return vals.toString();
  104.                 } else if (mName.startsWith("hashCode")) {
  105.                     return this.hashCode();
  106.                 }
  107.             }
  108.         } catch (Exception e) {
  109.             e.printStackTrace();
  110.         }
  111.         return null;
  112.     }
  113.     private Object convertVal(Type[] pts, Class<? retType, String fieldName) {
  114.         // 值获取不到时,初始化转换
  115.         Object retSrcVal = srcVals.get(fieldName);
  116.         if (retSrcVal != null) {
  117.             // 做值转换
  118.             Class<? cType = getComponentType((pts != null && pts.length  0) ? pts[0] : retType);
  119.             Object retVal = parseObjectInner(retType, retSrcVal, cType);
  120.             if (retVal != null) {
  121.                 vals.put(fieldName, retVal);
  122.             }
  123.             return retVal;
  124.         }
  125.         return null;
  126.     }
  127. }
  128. </code</pre

  129. 可优化地方

    1、日期(格式)处理
  130. 2、数值的处理,如科学计数法转换等
  131. 3、字符串编码处理,避免出现解析或者转换的乱码
  132. 4、格式化输出处理
  133. 5、性能和容错处理
  134. 总结

    1、理解Json数据结构基础上,通过与Java语言特性进行对应,实现Json解析器功能
  135. 2、划分类型,针对不同类型进行不同的处理,类型最终处理都归结为简单类型的处理
  136. 3、针对复合类型处理,需化繁为简进行处理
  137. 4、针对集合或者Map类型处理,首先要能够提取集合中对象的类型或者值,然后进行循环处理
  138. 5、理解代理实现类
  139. 参考

  140. Json介绍:http://www.json.org/
    阿里巴巴Fastjsonhttps://fastjson.codeplex.com/