Java 原生 JSON 序列化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangxin09/article/details/86688282

首先要说的是,笔者之前着实足够奇葩,净干别人未干过的事情,居然会想到用 JS 引擎来转换 JSON(《用 Rhino/Nashorn 代替第三方 JSON 转换库》《使用 Rhino 作为 Java 的 JSON 解析/转换包》),几经思考后,还是决然毅然放弃这个不切实际的想法,老老实实去写转换函数,几经打磨,有了下面“序列化” JSON 的 toJSON() 函数。

原理分析

请先过目源码。
完整源码在:
https://gitee.com/sp42_admin/ajaxjs/blob/master/ajaxjs-base/src/main/java/com/ajaxjs/js/JsonHelper.java

	/**
	 * 对 Object 尝试类型检测,将其合适地转换为 JSON 字符串返回。
	 * 
	 * @param obj 任意对象
	 * @return JSON 字符串
	 */
	public static String toJson(Object obj) {
		if (obj == null) {
			return null;
		} else if (obj instanceof String) {
			return '\"' + obj.toString().replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r") + '\"';
		} else if (obj instanceof Double) {
			return obj + "";
		} else if (obj instanceof Boolean || obj instanceof Number) {
			return obj.toString();
		} else if (obj instanceof Date) {
			return '\"' + CommonUtil.SimpleDateFormatFactory(CommonUtil.commonDateFormat).format((Date) obj) + '\"';
		} else if (obj.getClass() == Integer[].class) {
			return jsonArr((Integer[]) obj, v -> v + "");
		} else if (obj.getClass() == int[].class) {
			Integer[] arr = Arrays.stream((int[]) obj).boxed().toArray(Integer[]::new);
			return jsonArr(arr, v -> v + "");
		} else if (obj instanceof Long[]) {
			return jsonArr((Long[]) obj, v -> v.toString());
		} else if (obj instanceof long[]) {
			Long[] arr = Arrays.stream((long[]) obj).boxed().toArray(Long[]::new);
			return jsonArr(arr, v -> v.toString());
		} else if (obj instanceof String[]) {
			return jsonArr((String[]) obj, v -> "\"" + v + "\"");
		} else if (obj instanceof Map) {
			return stringifyMap((Map<?, ?>) obj);
		} else if (obj instanceof Map[]) {
			return jsonArr((Map<?, ?>[]) obj, JsonHelper::stringifyMap);
		} else if (obj instanceof BaseModel) {
			return beanToJson((BaseModel) obj);
		} else if (obj instanceof BaseModel[]) {
			return jsonArr((BaseModel[]) obj, JsonHelper::beanToJson);
		} else if (obj instanceof List) {
			List<?> list = (List<?>) obj;

			if (list.size() > 0) {
				if (list.get(0) instanceof Integer) {
					return toJson(list.toArray(new Integer[list.size()]));
				} else if (list.get(0) instanceof String) {
					return toJson(list.toArray(new String[list.size()]));
				} else if (list.get(0) instanceof Map) { // Map 类型的输出
					return toJson(list.toArray(new Map[list.size()]));
				} else if (list.get(0) instanceof BaseModel) { // Bean
					return toJson(list.toArray(new BaseModel[list.size()]));
				}
			} else {
				return "[]";
			}
		} else if (obj instanceof Object[]) {
			return jsonArr((Object[]) obj, JsonHelper::toJson);
		} else if (obj instanceof Object) { // 普通 Java Object
			List<String> arr = new ArrayList<>();
			for (Field field : obj.getClass().getDeclaredFields()) {
				field.setAccessible(true);

				String key = field.getName();
				if (key.indexOf("this$") != -1)
					continue;

				Object _obj = null;
				try {
					_obj = field.get(obj);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}

				arr.add('\"' + key + "\":" + toJson(_obj));
			}

			return '{' + String.join(",", arr) + '}';
		} else {
			throw new RuntimeException("不支持数据类型");
		}

		return null;
	}

这函数比较长,很多的 if else 判断,目的是一个方法包办所有常见类型到 JSON 的转换。他可以涵盖下面类型,作为输入的参数:

  • null
  • boolean/Boolean
  • String, String[], List<String>
  • int/Integer, int[]/Integer[], List<Integer>
  • long/Long, long[]/Long[], List<Long>
  • Date 日期
  • Map/Map[]/List<Map>
  • BaseModel/BaseModel[]/List<BaseModel> BaseModel 是我框架所有 Bean 的基类
  • Object, 普通 Java Object
    转换逻辑简单说明如下:首先 null 值自然返回 null;boolean 和 number 数字类型(包括 int/long 等等)转换字符串即可;字符串的话作适当的转义然后两旁套上双引号即可——这些都是比较简单的转换。

数组判断的话,首先有两种写法,是等价的,一种是 obj instanceof String[],另外一种是 obj.getClass() == int[].class,而且注意 Integer[] 和 int[] 类型的判断是不同的,要两种情况都要考虑。

JSON 中没有区分 Array 和 List,它只有 List。即使在 Java, Array 和 List 都离不开遍历的操作,我们把弄一块遍历好了。

/**
 * 输入任意类型数组,在 fn 作适当的转换,返回 JSON 字符串
 * 
 * @param o 数组
 * @param fn 元素处理器,返回元素 JSON 字符串
 * @return 数组的 JSON 字符串
 */
public static <T> String jsonArr(T[] o, Function<T, String> fn) {
	if (o.length == 0)
		return "[]";

	StringBuilder sb = new StringBuilder();

	for (int i = 0; i < o.length; i++) {
		sb.append(fn.apply((T) o[i]));
		if (i != (o.length - 1))
			sb.append(", ");
	}

	return '[' + sb.toString() + ']';
}

int[]/long[] 这些类型不能直接放在函数接口里面使用,因为 Java 的泛型就是要装箱类型的,故先转换一下,例如 Integer[] arr = Arrays.stream((int[]) obj).boxed().toArray(Integer[]::new);。值得一提的是,如果集合元素个数为零,那么返回空数组 [],而不是 null 或者空字符串。

Map 和 Bean 都有直接的转换函数,分别是 JsonHelper::stringifyMap 和 JsonHelper::beanToJson,都是些常规性的操作,比较多的是递归操作,其它亮点不多,——可自行看源码了解。若是数组的话则递归一下即可。

至于 List<?> 的判断,则是取出其第一个元素作为类型的判断,然后归纳到数组的处理方法中(你不能依然还是拿 List 去递归,那样会死循环)。

用法

下面是用法简介:

assertEquals(null, JsonHelper.toJson(null));
assertEquals("1.0", JsonHelper.toJson(1D));
assertEquals("true", JsonHelper.toJson(true));
assertEquals("1", JsonHelper.toJson(1));
assertEquals("1", JsonHelper.toJson(1L));
assertEquals("\"2018-02-20 00:00:00\"", JsonHelper.toJson((Date) MappingValue.objectCast("2018-2-20", Date.class)));

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
assertEquals("[1, 2, 3]", JsonHelper.toJson(list));
assertEquals("[1, 2, 3]", JsonHelper.toJson(new Integer[] { 1, 2, 3 }));
assertEquals("[1, 2, 3]", JsonHelper.toJson(new int[] { 1, 2, 3 }));

List<String> list2 = new ArrayList<>();
list2.add("1");
list2.add("2");
list2.add("3");
assertEquals("[\"1\", \"2\", \"3\"]", JsonHelper.toJson(list2));
assertEquals("[\"1\", \"2\", \"3\"]", JsonHelper.toJson(new String[] { "1", "2", "3" }));

Map<String, Object> map = new HashMap<>();
assertEquals("{}", JsonHelper.toJson(map));
map.put("foo", "bar");
assertEquals("{\"foo\":\"bar\"}", JsonHelper.toJson(map));
map.put("bar", 1);
assertEquals("{\"bar\":1,\"foo\":\"bar\"}", JsonHelper.toJson(map));

List<Map<String, Object>> list3 = new ArrayList<>();
assertEquals("[]", JsonHelper.toJson(list3));
list3.add(map);
list3.add(map);
assertEquals("[{\"bar\":1,\"foo\":\"bar\"}, {\"bar\":1,\"foo\":\"bar\"}]", JsonHelper.toJson(list3));

手工 JSON 输出:

/**
 * 检查是否重复的手机号码
 * 
 * @param phone 手机号码
 * @return true=已存在
 */
public String checkIfUserPhoneRepeat(String phone) {
	LOGGER.info("检查是否重复的手机号码:" + phone);
	return "{\"isRepeat\":" + getService().checkIfUserPhoneRepeat(phone) + "}";
}

转换为 toJson() 写法:

/**
 * 检查是否重复的手机号码
 * 
 * @param phone 手机号码
 * @return true=已存在
 */
public String checkIfUserPhoneRepeat(String phone) {
	LOGGER.info("检查是否重复的手机号码:" + phone);

	return toJson(new HashMap<String, Boolean>() {
		private static final long serialVersionUID = -5033049204280154615L;
		{
			put("isRepeat", getService().checkIfUserPhoneRepeat(phone));
		}
	});
}

可见通过一个 HashMap 来作为健对值的容器了。

普通 Java Object 转换 JSON

普通 Java Object 也可以表达 Key、Value 的结构,如下列,

@Test
public void testStringifySimpleObject() {
	Object obj = new Object() {
		@SuppressWarnings("unused")
		public Object NULL = null;
		@SuppressWarnings("unused")
		public String str = null;
		@SuppressWarnings("unused")
		public Boolean isOk = false;
		@SuppressWarnings("unused")
		public Integer n0 = 0;
		@SuppressWarnings("unused")
		public Number n1 = 111;
		@SuppressWarnings("unused")
		public int n2 = 222;
		// @SuppressWarnings("unused")
		// public Date date = new Date();
		@SuppressWarnings("unused")
		public String msg = "Hello world";
		@SuppressWarnings("unused")
		public Object[] arr = new Object[] { 1, "2", null };
	};

	String jsonStr = JsonHelper.toJson(obj);
	// 输出 {"foo":"11","bar":"2222"}
	assertNotNull(jsonStr);
	assertEquals("{\"NULL\":null,\"str\":null,\"isOk\":false,\"n0\":0,\"n1\":111,\"n2\":222,\"msg\":\"Hello world\",\"arr\":[1, \"2\", null]}", jsonStr);
}

toJSON() 也是支持这种结构的。

展开阅读全文

没有更多推荐了,返回首页