Java 算法 JSON Bean相互转化及JSON生成实体类

转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/50495677;
本文出自:【Kevin.zhou的博客】

前言:之前解析JSON数据的时候使用的是GSON,相信大家已经非常熟悉,在封装开源控件的时候觉得GSON还是太重了而且别人在使用的时候不一定用这个解析框架,那就自己写一个解析的工具吧。

一、概述


    将JSON封装到Bean对象,就是将JSON所对应的数据一一赋值到实例对象上,那么可以逆向过来,看该Bean对象有哪些字段,然后用字段的名称去JSON中去查找值,再将查询到的赋值到该字段。

二、JSON语法


    也行大家向我之前一样对于JSON的语法不太了解,知道客户端去访问后台提供的接口,后台返回给我JSON字符串,我写一个实体对象,然后使用工具(GSON、Jackson、Json-lib等)一行代码就封装到了对象上。既然我们要自己编写解析的话就要简单了解下JSON究竟是怎样定义的。

    1. JSON 数据的书写格式
  •     数据在名称/值对中
  •     数据由逗号分隔
  •     花括号保存对象
  •     方括号保存数组
    
    2. JSON的值
  •     数字(整数或浮点数)
  •     字符串(在双引号中)
  •     逻辑值(true 或 false)
  •     数组(在方括号中)
  •     对象(在花括号中)
  •     null

    3. JSON 对象
    JSON 对象在花括号中书写:
    对象可以包含多个名称/值对:
 { "firstName":"Zhou" , "lastName":"Kevin" }

    4. JSON 数组
    JSON 数组在方括号中书写:
    数组可包含多个对象:
{
	"employees": [
		{
			"firstName": "Zhou",  "lastName": "Kevin" 
		},
		{
			"firstName": "Li",  "lastName": "Qian" 
		},
		{
			"firstName": "Peter",  "lastName": "Jones" 
		}
	]
}

三、反射的简单回顾


1. 根据类字节码获取实例
    假设有一个类 T 以及它的字节码 clazz        
T t = clazz.newInstance();

这样调用的是该类无参的构造函数,创建了实例对象。
        
    上面的方式对于内部类就无计可施了,我们大家都知道创建内部类对象的时候需要外部类引用(指针),
    假设有这么一个类:
public class Outer {
	public class Inner{
	}
}

那么我们想要创建Inner的实例对象就要按照以下的方式:

Outer outer = new Outer();
Inner inner = outer.new Inner();

当然在利用反射时候也需要有外部类的引用(指针),如下:

Outer outer = Outer.class.newInstance();
Constructor<?> constructor = Inner.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Inner inner = (Inner) constructor.newInstance(outer);

 2. 根据类字节码获取所有字段

Field[] fields = c.getDeclaredFields();

3. 获取某一字段的类型名称
    String name = field.getType().getName();
    String类型字段类型名称:java.lang.String
    int类型字段类型名称:int
    Integer类型字段类型名称:java.lang.Integer
    boolean类型字段类型名称:boolean
    Boolean类型字段类型名称:java.lang.Boolean
    float类型字段类型名称:float
    Float类型字段类型名称:java.lang.Float
    double类型字段类型名称:double
    Double类型字段类型名称:java.lang.Double
    List类型字段类型名称:java.util.List
    ArrayList类型字段类型名称:java.util.ArrayList
    其他的为类 类型字段名称:如 com.kevin.jsontool.bean.TestBean2$LoopData为内部类

4.设置某一字段的值
    假设有如下类:
public class Person{
	public String name;
	public int age;
}

Pserson p = Person.class.newInstance();
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
	   field.setAccessible(true);
	   Class<?> type = field.getType();
	   String typeName = type.getName();
	   if(typeName.equalsIgnoreCase("java.lang.String")) {
			field.set(p, "Kevin");
		} else if(typeName.equalsIgnoreCase("int")) {
			field.set(p, 25);
		}
}

四、JSONTool toBean编写


    在前文提到过,是反射实例对象的字段然后去JSONObject查找对应的值,当然也可以在字段的头上加上自己的注解这样可以字段的名称可以和JSONObject中名称(键)不严格一致,这里不去做了。   
/**
 * 将JSON字符串封装到对象
 * 
 * @param jsonStr 待封装的JSON字符串
 * @param clazz 待封装的实例字节码
 * @return T: 封装JSON数据的对象
 * @version 1.0
 */
public static <T> T toBean(String jsonStr, Class<T> clazz) {
	try {
		  JSONObject job = new JSONObject(jsonStr);
		  return parseObject(job, clazz, null);
	} catch (JSONException e) {
		  e.printStackTrace();
	}
	return null;
}

/**
 * JSONObject 封装到 对象实例
 * 
 * @param job 待封装的JSONObject
 * @param c 待封装的实例对象class
 * @param v	待封装实例的外部类实例对象</br>只有内部类存在,外部类时传递null
 * @return T:封装数据的实例对象
 * @version 1.0 
 * @date 2015-10-9
 * @Author zhou.wenkai
 */
@SuppressWarnings("unchecked")
private static <T, V> T parseObject(JSONObject job, Class<T> c, V v) {
	T t = null;
	try {
		if(null == v) {
			t = c.newInstance();
		} else {
			Constructor<?> constructor = c.getDeclaredConstructors()[0];
			constructor.setAccessible(true);
			t = (T) constructor.newInstance(v);
		}
	} catch (IllegalArgumentException e) {
		e.printStackTrace();
		Log.e(JsonTool.class.getSimpleName(),
				c.toString() + " should provide a default constructor " +
						"(a public constructor with no arguments)");
	} catch (Exception e) {
		if(DEBUG)
			e.printStackTrace();
	}
	
	Field[] fields = c.getDeclaredFields();
	for (Field field : fields) {
		field.setAccessible(true);
		Class<?> type = field.getType();
		String name = field.getName();
		
		// if the object don`t has a mapping for name, then continue
		if(!job.has(name)) continue;
		
		String typeName = type.getName();
		if(typeName.equals("java.lang.String")) {
			try {
				String value = job.getString(name);
				if (value != null && value.equals("null")) {
					value = "";
				}
				field.set(t, value);
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
				try {
					field.set(t, "");
				} catch (Exception e1) {
					if(DEBUG)
						e1.printStackTrace();
				}
			}
		} else if(typeName.equals("int") ||
				typeName.equals("java.lang.Integer")) {
			try {
				field.set(t, job.getInt(name));
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("boolean") ||
				typeName.equals("java.lang.Boolean")) {
			try {
				field.set(t, job.getBoolean(name));
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("float") ||
				typeName.equals("java.lang.Float")) {
			try {
				field.set(t, Float.valueOf(job.getString(name)));
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("double") || 
				typeName.equals("java.lang.Double")) {
			try {
				field.set(t, job.getDouble(name));
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("long") || 
				typeName.equals("java.lang.Long")) {
			try {
				field.set(t, job.getLong(name));
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("java.util.List") ||
				typeName.equals("java.util.ArrayList")){
			try {
				Object obj = job.get(name);
				Type genericType = field.getGenericType();
				String className = genericType.toString().replace("<", "")
						.replace(type.getName(), "").replace(">", "");
				Class<?> clazz = Class.forName(className);
				if(obj instanceof JSONArray) {
					ArrayList<?> objList = parseArray((JSONArray)obj, clazz, t);
					field.set(t, objList);
				}
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else {
			try {
				Object obj = job.get(name);
				Class<?> clazz = Class.forName(typeName);
				if(obj instanceof JSONObject) {
					Object parseJson = parseObject((JSONObject)obj, clazz, t);
					field.set(t, parseJson);
				}
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
			
		}
	}

	return t;
}

/**
 * 将 JSONArray 封装到 ArrayList 对象
 * 
 * @param array 待封装的JSONArray
 * @param c 待封装实体字节码
 * @param v 待封装实例的外部类实例对象</br>只有内部类存在,外部类时传递null
 * @return ArrayList<T>: 封装后的实体集合
 * @version 1.0 
 * @date 2015-10-8
 */
@SuppressWarnings("unchecked")
private static <T, V> ArrayList<T> parseArray(JSONArray array, Class<T> c, V v) {
	ArrayList<T> list = new ArrayList<T>(array.length());
	try {
		for (int i = 0; i < array.length(); i++) {
			if(array.get(i) instanceof JSONObject) {
				T t = parseObject(array.getJSONObject(i), c, v);
				list.add(t);
			} else {
				list.add((T) array.get(i));
			}
			
		}
	} catch (Exception e) {
		if(DEBUG)
			e.printStackTrace();
	}
	return list;
}

五、JSONTool toJson编写


    由实体类封装到JSON数据,就是遍历该实体类的所以字段,然后找到字段的名称以及字段值然后封装JSON数据。

/**
 * 将 对象编码为 JSON格式
 * 
 * @param t 待封装的对象
 * @return String: 封装后JSONObject String格式
 * @version 1.0
 */
public static <T> String toJson(T t) {
	if (t == null) {
		return "{}";  
	}
	return objectToJson(t);
}

/**
 * 将 对象编码为 JSON格式
 * 
 * @param t 待封装的对象
 * @return String: 封装后JSONObject String格式
 * @version 1.0 
 * @date 2015-10-11
 * @Author zhou.wenkai
 */
private static <T> String objectToJson(T t) {
	
	Field[] fields = t.getClass().getDeclaredFields();
	StringBuilder sb = new StringBuilder(fields.length << 4);
	sb.append("{");
	
	for (Field field : fields) {
		field.setAccessible(true);
		Class<?> type = field.getType();
		String name = field.getName();
		
		// 'this$Number' 是内部类的外部类引用(指针)字段
		if(name.contains("this$")) continue;
		
		String typeName = type.getName();
		if(typeName.equals("java.lang.String")) {
			try {
				sb.append("\""+name+"\":");
				sb.append(stringToJson((String)field.get(t)));
				sb.append(",");
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("boolean") ||
				typeName.equals("java.lang.Boolean") ||
				typeName.equals("int") ||
				typeName.equals("java.lang.Integer") ||
				typeName.equals("float") ||
				typeName.equals("java.lang.Float") ||
				typeName.equals("double") || 
				typeName.equals("java.lang.Double") ||
				typeName.equals("long") || 
				typeName.equals("java.lang.Long")) {
			try {
				sb.append("\""+name+"\":");
				sb.append(field.get(t));
				sb.append(",");
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else if(typeName.equals("java.util.List") ||
				typeName.equals("java.util.ArrayList")){
			try {
				List<?> objList = (List<?>) field.get(t);
				if(null != objList && objList.size() > 0) {
					sb.append("\""+name+"\":");
					sb.append("[");
					String toJson = listToJson((List<?>) field.get(t));
					sb.append(toJson);
					sb.setCharAt(sb.length()-1, ']');
					sb.append(",");
				}
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		} else {
			try {
				sb.append("\""+name+"\":");
				sb.append("{");
				sb.append(objectToJson(field.get(t)));
				sb.setCharAt(sb.length()-1, '}');
				sb.append(",");
			} catch (Exception e) {
				if(DEBUG)
					e.printStackTrace();
			}
		}
		
	}
	if(sb.length() == 1) {
		sb.append("}");
	}
	sb.setCharAt(sb.length()-1, '}');
	return sb.toString();
}

/**
 * 将 List 对象编码为 JSON格式
 * 
 * @param objList 待封装的对象集合
 * @return String:封装后JSONArray String格式
 * @version 1.0 
 * @date 2015-10-11
 * @Author zhou.wenkai
 */
private static<T> String listToJson(List<T> objList) {
	final StringBuilder sb = new StringBuilder();
	for (T t : objList) {
		if(t instanceof String) {
			sb.append(stringToJson((String) t));
			sb.append(",");
		} else if(t instanceof Boolean || 
				t instanceof Integer ||
				t instanceof Float ||
				t instanceof Double) {
			sb.append(t);
			sb.append(",");
		} else {
			sb.append(objectToJson(t));
			sb.append(",");
		}
	}
	return sb.toString();
}

/**
 * 将 String 对象编码为 JSON格式,只需处理好特殊字符
 * 
 * @param str String 对象
 * @return String:JSON格式 
 * @version 1.0 
 * @date 2015-10-11
 * @Author zhou.wenkai
 */
private static String stringToJson(final String str) {
	if(str == null || str.length() == 0) {
		return "\"\"";
	}
	final StringBuilder sb = new StringBuilder(str.length() + 2 << 4);
	sb.append('\"');
	for (int i = 0; i < str.length(); i++) {
		final char c = str.charAt(i);

		sb.append(c == '\"' ? "\\\"" : c == '\\' ? "\\\\"
				: c == '/' ? "\\/" : c == '\b' ? "\\b" : c == '\f' ? "\\f"
						: c == '\n' ? "\\n" : c == '\r' ? "\\r"
								: c == '\t' ? "\\t" : c);
	}
	sb.append('\"');
	return sb.toString();
}

六、JSONTool createBean编写


    由JSON数据生成实体类,这里生成的实体类为String类型。原理及时遍历JSON数据,根据数据的类型以及名称来生成实体类。

/**
 * 由JSON字符串生成Bean对象
 *  
 * @param jsonStr
 * @param className 待生成Bean对象的名称
 * @return String:
 * @version 1.0
 */
public static String createBean(String jsonStr, String className) {
	try {
		JSONObject job = new JSONObject(jsonStr);
		return createObject(job, className, 0);
	} catch (JSONException e) {
		e.printStackTrace();
	}
	return "";
}

	/**
	 * 由JSONObject生成Bean对象
	 * 
	 * @param job
	 * @param className 待生成Bean对象的名称
	 * @param outCount 外部类的个数
	 * @return LinkedList<String>: 生成的Bean对象
	 * @version 1.0
	 * @date 2015-10-16
	 * @Author zhou.wenkai
	 */
	private static String createObject(JSONObject job, String className, int outCount) {
		final StringBuilder sb = new StringBuilder();
		String separator = System.getProperty("line.separator");
		
		// 生成的Bean类前部的缩进空间
		String classFrontSpace = "";
		// 生成的Bean类字段前部的缩进空间
		String fieldFrontSpace = "    ";
		for (int i = 0; i < outCount; i++) {
			classFrontSpace += "    ";
			fieldFrontSpace += "    ";
		}
		
		sb.append(classFrontSpace + "public class " + className + " {");
		
		Iterator<?> it = job.keys();
		while (it.hasNext()) {
            String key = (String) it.next();
            try {
				Object obj = job.get(key);
				if(obj instanceof JSONArray) {
					// 判断类是否为基本数据类型,如果为自定义类则字段类型取将key的首字母大写作为内部类名称
					String fieldType = ((JSONArray)obj).get(0) instanceof JSONObject ?
							"" : ((JSONArray)obj).get(0).getClass().getSimpleName();
					if(fieldType == "") {
						fieldType = String.valueOf(Character.isUpperCase(key.charAt(0)) ? 
								key.charAt(0) : Character.toUpperCase(key.charAt(0))) + key.substring(1);
					}
					sb.append(separator);
					sb.append(fieldFrontSpace + "public List<" + fieldType + "> " + key + ";");
					
					// 如果字段类型为自定义类类型,则取JSONArray中第一个JSONObject生成Bean
					if(((JSONArray)obj).get(0) instanceof JSONObject) {
						sb.append(separator);
						sb.append(separator);
						sb.append(fieldFrontSpace + "/** "+ fieldType +" is the inner class of "+ className +" */");
						sb.append(separator);
						sb.append(createObject((JSONObject)((JSONArray)obj).get(0), fieldType, outCount+1));
					}
				} else if(obj instanceof JSONObject) {
					String fieldType = String.valueOf(Character.isUpperCase(key.charAt(0)) ? 
							key.charAt(0) : Character.toUpperCase(key.charAt(0))) + key.substring(1);
					sb.append(separator);
					sb.append(fieldFrontSpace + "public List<" + fieldType + "> " + key + ";");
					sb.append(separator);
					sb.append(separator);
					sb.append(fieldFrontSpace + "/** "+ fieldType +" is the inner class of "+ className +" */");
					sb.append(separator);
					sb.append(createObject((JSONObject)obj, fieldType, outCount+1));
				} else {
					String type = obj.getClass().getSimpleName();
					sb.append(separator);
					sb.append(fieldFrontSpace + "public " + type + " " + key + ";");
				}
			} catch (JSONException e) {
				e.printStackTrace();
			}
        }
		
		sb.append(separator);
		sb.append(classFrontSpace + "}");
		sb.append(separator);
		
		return sb.toString();
	}

七、源码及示例

   
    给大家提供一个github的地址: Android-JSONTool
    另外,欢迎 star or f**k me on github!  


八、一行引入库

    

如果您的项目使用 Gradle 构建, 只需要在您的build.gradle文件添加下面一行到 dependencies :

compile 'com.kevin:jsontool:1.0.0'

九、结语


    相信大家了解了JSON格式以及和实体类的封装原理后也能轻松编写出自己的解析。

你可能感兴趣的:(json,Bean转换,JSON生成Bean)