json模块的包是:org.nutz.json,下文中涉及到的类名都是从该包路径说起。
json模块的大致功能包括以下几点:
- Json类将java对象和字符串进行互相转换。
- 定义了json的注解。包括:Jsonield,JsonIgnore,ToJson
- 针对json的输出进行了一些格式的控制,涉及类包括:JsonFormat
- 异常处理:JsonException
- 其他定义的一些接口和具体工具类,这里不一一列觉。
- 该模块的结构图如下所示:
Json类将java对象和字符串进行互相转换
该类主要通过fromJson和toJson两类方法来完成java对象与字符串之间的相互转换。 主要方法入下图所示:
fromJson方法
- fromJson方法进行了多次重载,接收参数可以是字符串,各种输入流,文件.
- 另外也提供了fromJsonAsArray方法用于将json字符串转换为java列表。设计和用法与fromJson没有太大差别。
- 返回结果可以是指定类型的java对象,或者Map对象(Map对象中可能继续嵌套Map或者List对象)
-
具体转换步骤
- 转换过程中用到一些工具类,比如文件打开和关闭,字符对象转换,这些类都在org.nutz.lang模块中,这些以后读到相应的模块再深入了解。
- fromJson调用的是impl.JsonCompileImplV2类的parse方法。
- 而这个parse方法调用了JsonTokenScan的read方法。JsonTokenScan 类是解析json字符串,并返回一个Map-List对象的核心实现;
- 最后在Json类中调用Mapl 工具类将Map-List对象转换为指定的java对象
- JsonTokenScan 中通过顺序读取json字符,根据字符特征来标记一个Map或者List的开始或者结束,将一个个属性存储在Map或者List对象中,并嵌套起来。这里是字符串转换为对象的核心算法实现。点击查看具体代码
change from 2015-01-02
toJson方法
- toJson方法同样进行了重载,提供了将java丢想转换为字符串,输出到指定的输出流,文件等,输出的时候可以指定字符串格式,格式有传入的Jsonromat规定。
-
所有重载的toson方法最终都是调用:toJson(Writer writer, Object obj, JsonFormat format),该方法调用Jsonender接口的render将java对象输出到制定的输出流中,该接口有一个默认实现:JsonRenderImpl,接下来看它的render方法。
public void render(Object obj) throws IOException { if (null == obj) { writer.write("null"); } else if (obj instanceof JsonRender) { ((JsonRender) obj).render(null); } else if (obj instanceof Class) { string2Json(((Class<?>) obj).getName()); } else if (obj instanceof Mirror) { string2Json(((Mirror<?>) obj).getType().getName()); } else { Mirror mr = Mirror.me(obj.getClass()); // 枚举 if (mr.isEnum()) { string2Json(((Enum) obj).name()); } // 数字,布尔等 else if (mr.isNumber()) { String tmp = obj.toString(); if (tmp.equals("NaN")) { // TODO 怎样才能应用上JsonFormat中是否忽略控制呢? // 因为此时已经写入了key: writer.write("null"); } else writer.write(tmp); } else if (mr.isBoolean()) { writer.append(obj.toString()); } // 字符串 else if (mr.isStringLike() || mr.isChar()) { string2Json(obj.toString()); } // 日期时间 else if (mr.isDateTimeLike()) { boolean flag = true; if (obj instanceof Date) { DateFormat df = format.getDateFormat(); if (df != null) { string2Json(df.format((Date)obj)); flag = false; } } if (flag) string2Json(format.getCastors().castToString(obj)); } // 其他 else { // Map if (obj instanceof Map) { map2Json((Map) obj); } // 集合 else if (obj instanceof Collection) { coll2Json((Collection) obj); } // 数组 else if (obj.getClass().isArray()) { array2Json(obj); } // 普通 Java 对象 else { memo.add(obj); pojo2Json(obj); memo.remove(obj); } } } }
- 从上面可以看到该方法,主要针对要转换为json字符串的对象的类型进行判断,然后根据不同类型采用不同的处理方法。
-
针对各种基本类型的处理方法是将其转换为String类型,然后调用string2Json输出为json字符串。在string2Json方法中,一个一个字符迭代,根据字符标识,进行输出
<code> private void string2Json(String s) throws IOException { if (null == s) writer.append("null"); else { char[] cs = s.toCharArray(); writer.append(format.getSeparator()); for (char c : cs) { switch (c) { case '"': writer.append("\\\""); break; case '\n': writer.append("\\n"); break; case '\t': case 0x0B: // \v writer.append("\\t"); break; case '\r': writer.append("\\r"); break; case '\f': writer.append("\\f"); break; case '\b': writer.append("\\b"); break; case '\\': writer.append("\\\\"); break; default: if (c >= 256 && format.isAutoUnicode()) { writer.append("\\u"); String u = Strings.fillHex(c, 4); if (format.isUnicodeLower()) writer.write(u.toLowerCase()); else writer.write(u.toUpperCase()); } else { if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) { writer.write("\\u"); String hhhh = Integer.toHexString(c); writer.write("0000", 0, 4 - hhhh.length()); writer.write(hhhh); } else { writer.append(c); } } } } writer.append(format.getSeparator()); } }
</code>
- 针对map,list,数组调用相应的map2Json,coll2Json,array2Json方法输出。
- 然后是最重要的,针对普通的pojo对象进行输出,
- 该方法为:pojo2Json,该方法主要使用反射一一获取pojo对象的属性,放在列表中,然后迭代列表,继续调用render进行处理,这样递归调用render方法,直到所有属性都被转换为基本数据类型,或者集合(集合中是基本数据类型);最后调用render方法中针对基本数据类型和集合(map,list,array)的处理方法输出json字符串。
- 这个过程使用了几个工具类:
- entity.JsonEntity:用于描述java对象映射为json字符串的规则(参考api)比如当前java对象有哪几个属性,分别什么类型等等。
- org.nutz.lang.Mirror:该类提供了一些反射相关操作方法的封装。
-