之前做过一个项目需要使用json来传输数据,我用的是org.json,但是其构造和解析方法过于繁琐,想到之前解析xml时用的simpleXml可以通过注解的方式构造和解析xml文本,
我就参考simpleXml,利用java的反射机制做了一个基于org.json的工具包,这个工具同样可以通过注解来构造json。
首先是一个接口,这个接口是个标记,表名一个类可以使用这个工具转换json
package dikaros.json.annotation;
/**
* 动态转换标记
* 表示该实例类可以被动态转换成json信息
* @author Dikaros
*
*/
public interface Jsonable {
}
package dikaros.json.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* JsonParam 标准类型注解用在类的基本属性前
* 在生成json文本后为键值对的key
* @author Dikaros
*
*/
//注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonParam {
/**
* json文本 键
* @return
*/
public String name() default "";
}
注解JsonListParam
package dikaros.json.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* JsonParam 标准类型注解用在类的基本属性前
* 在生成json文本后为键值对的key
* className表示数组类型或集合的泛型如String.class
* 枚举类型TYPE表示数组类型ARRAY表示数组,例如int[], LIST表示集合,例如java.util.List
*
* @author Dikaros
* @see TYPE
*
*/
// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonListParam {
public String name() default "";
/**
* 数组类型或集合泛型
* @return
*/
public Class contentClassName();
/**
* 设置是集合还是数组
* @return TYPE.ARRAY
或TYPE.LIST
* @see TYPE
*/
public TYPE classType() default TYPE.LIST;
/**
* 多数据类型
* ARRAY表示数组,例如int[]
* LIST表示集合,例如java.util.List
* 默认为LIST
*
* @author Dikaros
*
*/
public enum TYPE {
ARRAY, LIST
};
}
package dikaros.json;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import dikaros.json.annotation.JsonListParam;
import dikaros.json.annotation.JsonParam;
import dikaros.json.annotation.Jsonable;
import dikaros.json.annotation.JsonListParam.TYPE;
import dikaros.org.json.JSONArray;
import dikaros.org.json.JSONException;
import dikaros.org.json.JSONObject;
import dikaros.org.json.JSONStringer;
/**
* org.json扩展
* 将类构造成简单的json文本
* 注意:构造的类需要实现Jsonable接口,并且是标准的JavaBean
* 原理:通过反射机制获取需要转换成json对象的注解,根据注解的内容生成key
* 由于注解是添加在变量上的,如果类拥有标准get和set方法,则通过反射获取
* 并执行这些方法即可当作value
* 建议:将一些类型改变为基本类型,如java.util.Date用long来表示
*
* @author Dikaros
* @see Jsonable
* @see JsonParam
* @see JsonListParam
*/
public class JsonExtendedUtil {
/**
* 构造一个对象的json
*
* @param object
* 需要转换成json的对象,需要实现Jsonable接口
* @return json文本
* @throws Exception
*/
public static String generateJson(Object object) throws Exception {
JSONStringer stringer = new JSONStringer();
stringer.array();
generateObjectMessage(object, stringer);
stringer.endArray();
return stringer.toString();
}
/**
* 构造json
* 原理:通过反射机制获取注解并将其作为key,再调用field的get方法即可得到value
*
* @param object
* 需要转换成json的对象,需要实现Jsonable接口
* @param stringer
* org.json的json构造器
* @throws Exception
*/
private static void generateObjectMessage(Object object,
JSONStringer stringer) throws Exception {
// 如果object没有实现Jsonable接口,则抛出异常
if (!(object instanceof Jsonable)) {
throw new Exception("不支持的类型格式,是否忘记了实现JsonAble接口");
}
// 开始构造json对象
stringer.object();
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(JsonParam.class)) {
JsonParam jsonParam = field.getAnnotation(JsonParam.class);
// json键
stringer.key(jsonParam.name());
Method method = getFieldMethod(clazz, field);
Object value = method.invoke(object, new Object[] {});
// json值
stringer.value(value);
} else if (field.isAnnotationPresent(JsonListParam.class)) {
// System.out.println("list");
JsonListParam jsonListParam = field
.getAnnotation(JsonListParam.class);
// 字段名称,根据字段名称获取其对应的get方法
Method method = getFieldMethod(clazz, field);
Object value = method.invoke(object, new Object[] {});
if (value == null) {
continue;
}
if (jsonListParam.classType() == TYPE.ARRAY) {
Array.getLength(value);
// 调用get方法
stringer.key(jsonListParam.name());
// 集合的键
stringer.array();
// 集合字段
for (int i = 0; i < Array.getLength(value); i++) {
Object listItem = Array.get(value, i);
// 如果集合是JsonAble的实例则调用此函数
if ((listItem instanceof Jsonable)) {
generateObjectMessage(listItem, stringer);
}
// 如果集合不是JsonAble的实例
else if (isBaseType(listItem)) {
stringer.value(listItem);
} else {
throw new Exception("集合元素类型不支持,请将集合内元素实现JsonAble接口");
}
}
// 集合结束
stringer.endArray();
} else if (jsonListParam.classType() == TYPE.LIST) {
// 调用get方法
stringer.key(jsonListParam.name());
// 集合的键
stringer.array();
// 集合字段
List list = (List) value;
for (Object listItem : list) {
// 如果集合是JsonAble的实例则调用此函数
if ((listItem instanceof Jsonable)) {
generateObjectMessage(listItem, stringer);
}
// 如果集合不是JsonAble的实例
else if (isBaseType(listItem)) {
stringer.value(listItem);
} else {
throw new Exception("集合元素类型不支持,请将集合内元素实现JsonAble接口");
}
}
// 集合结束
stringer.endArray();
}
}
}
// json对象结束
stringer.endObject();
}
/**
* 判断对象是否是基本类型
*
* @return true是,false 不是
*/
private static boolean isBaseType(Object obj) {
boolean result = false;
if ((obj instanceof Integer) || (obj instanceof Byte)
|| (obj instanceof Double) || (obj instanceof Boolean)
|| (obj instanceof String) || (obj instanceof Short)
|| (obj instanceof Long) || (obj instanceof Character)
|| (obj instanceof Float)) {
result = true;
}
return result;
}
/**
* 获取JavaBean字段的get方法
*/
private static Method getFieldMethod(Class clazz, Field field)
throws Exception {
String fieldName = field.getName();
fieldName = fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
Method method = null;
try {
if (field.getType() == Boolean.class) {
method = clazz.getMethod("is" + fieldName, null);
} else {
method = clazz.getMethod("get" + fieldName, null);
}
} catch (NoSuchMethodException e) {
throw new Exception("不是标准的JavaBean,注意JavaBean的格式");
}
return method;
}
/**
* 获取JavaBean字段的set方法
*/
@SuppressWarnings("unchecked")
private static Method setFieldMethod(Class clazz, Field field)
throws Exception {
String fieldName = field.getName();
fieldName = fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
Method method = null;
try {
// System.out.println("set---" + fieldName);
method = clazz.getMethod("set" + fieldName,
new Class[] { field.getType() });
} catch (NoSuchMethodException e) {
throw new Exception("不是标准的JavaBean,注意JavaBean的格式");
}
return method;
}
public static Object compileJson(Class clazz, String jsonFile)
throws Exception {
JSONArray array = new JSONArray(jsonFile);
Object object = null;
if (array.length() > 0) {
JSONObject jsonObject = array.getJSONObject(0);
object = clazz.newInstance();
getObjectFromJsonFile(object, jsonObject.toString());
}
return object;
}
/**
* 解析Json
* 原理:传入Object获取其类型,通过其类型的注解调用属性的set方法更改Object的值
* 注意:该方法的传入json是一个JSONObject型的
*
* @param jsonable
* 待转换的类
* @param jsonFile
* 需要解析的jsonFile
* @throws Exception
*/
private static void getObjectFromJsonFile(Object jsonable, String jsonFile)
throws Exception {
if (!(jsonable instanceof Jsonable)) {
throw new Exception("不支持的类型格式,是否忘记了实现JsonAble接口");
}
// 获取class
Class clazz = jsonable.getClass();
JSONObject obj = new JSONObject(jsonFile);
// 获取类所有的属性
Field[] fields = clazz.getDeclaredFields();
// 遍历所有的属性
for (Field field : fields) {
/*
* 如果属性中存在JsonParam的注解 从JsonFile中获取内容 通过反射机制调用set方法更新属性
*/
if (field.isAnnotationPresent(JsonParam.class)) {
JsonParam jsonParam = field.getAnnotation(JsonParam.class);
String keyName = jsonParam.name();
Object value = obj.get(keyName);
Method method = setFieldMethod(clazz, field);
// 调用set方法更新属性
method.invoke(jsonable, new Object[] { value });
}
/*
* 如果属性中存在JsonListParam的注解 从JsonFile中获取内容 判断多值类型是集合还是数组
* 根据类型采用不同的方法更新属性
*/
else if (field.isAnnotationPresent(JsonListParam.class)) {
JsonListParam jsonListParam = field
.getAnnotation(JsonListParam.class);
// 获取键
String keyName = jsonListParam.name();
JSONArray jArray = null;
/*
* 判断有没有这样的keyName 如果没有,跳过进行下一次循环
*/
try {
jArray = obj.getJSONArray(keyName);
} catch (Exception e) {
continue;
}
if (!(field.getType().equals(List.class))
&& !field.getType().isArray()) {
throw new Exception("类型错误,类型不是List的实例");
}
Method method = setFieldMethod(clazz, field);
TYPE classType = jsonListParam.classType();
/*
* 如果是集合 构建集合 递归调用此方法进行设置
*/
if (classType == TYPE.LIST) {
List list = new ArrayList<>();
for (int i = 0; i < jArray.length(); i++) {
JSONObject item = jArray.getJSONObject(i);
Class itemClass = jsonListParam.contentClassName();
Object itemObject = itemClass.newInstance();
if (itemObject instanceof Jsonable) {
getObjectFromJsonFile(itemObject, item.toString());
list.add(itemObject);
} else if (isBaseType(itemObject)) {
list.add(jArray.get(i));
}
}
method.invoke(jsonable, new Object[] { list });
}
/*
* 如果是数组 根据JSONArray大小构建数组 递归调用此方法进行设置
*/
else if (classType == TYPE.ARRAY) {
// Object[] items = new Object[jArray.length()];
Object items = Array.newInstance(
jsonListParam.contentClassName(), jArray.length());
for (int i = 0; i < jArray.length(); i++) {
Class itemClass = jsonListParam.contentClassName();
Object itemObject = itemClass.newInstance();
if (itemObject instanceof Jsonable) {
JSONObject item = jArray.getJSONObject(i);
getObjectFromJsonFile(itemObject, item.toString());
// items[i] = itemObject;
Array.set(items, i, itemObject);
} else if (isBaseType(itemObject)) {
Array.set(items, i, jArray.get(i));
}
}
System.out.println("method--->" + method.getName() + ":"
+ method.getParameterTypes()[0]);
method.invoke(jsonable, new Object[] { items });
}
}
}
}
/**
* 获取long值
*
* @param jsonFile
* @param param
* @return
*/
public static long getLongParam(String jsonFile, String param) {
long result = 0;
JSONObject obj;
JSONArray array;
try {
array = new JSONArray(jsonFile);
if (array.length() > 0) {
obj = array.getJSONObject(0);
result = obj.getLong(param);
}
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
/**
* 获取boolean值
*
* @param jsonFile
* @param param
* @return
*/
public static boolean getBooleanParam(String jsonFile, String param) {
boolean result = false;
JSONObject obj;
JSONArray array;
try {
array = new JSONArray(jsonFile);
if (array.length() > 0) {
obj = array.getJSONObject(0);
result = obj.getBoolean(param);
}
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
/**
* 获取指定字段的内容
*
* @param jsonFile
* @param param
* @return
*/
public static String getParam(String jsonFile, String param) {
String result = null;
JSONObject obj;
JSONArray array;
try {
array = new JSONArray(jsonFile);
if (array.length() > 0) {
obj = array.getJSONObject(0);
result = obj.getString(param);
}
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
/**
* 从json文件中获取整型值
*
* @param jsonFile
* @param param
* @return
*/
public static int getIntParam(String jsonFile, String param) {
int result = -1;
JSONObject obj;
JSONArray array;
try {
array = new JSONArray(jsonFile);
if (array.length() > 0) {
obj = array.getJSONObject(0);
result = obj.getInt(param);
}
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
/**
* 构建 传输信息
*
* @param map
* @return
*/
public static String constructMapJson(HashMap map) {
Set set = map.keySet();
JSONStringer jsonStringer = new JSONStringer();
try {
jsonStringer.array();
jsonStringer.object();
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
jsonStringer.key(key);
jsonStringer.value(map.get(key));
}
jsonStringer.endObject();
jsonStringer.endArray();
} catch (JSONException e) {
e.printStackTrace();
}
return jsonStringer.toString();
}
}
假定有一个类Person,注意,由于利用的是java的反射机制,所以这个类一定要是标准的JavaBean,被注解的属性必须有getter和setter方法。
package dikaros.example;
import java.util.Arrays;
import java.util.List;
import dikaros.json.annotation.JsonListParam;
import dikaros.json.annotation.JsonParam;
import dikaros.json.annotation.Jsonable;
import dikaros.json.annotation.JsonListParam.TYPE;
/**
* 示例对象
* @author Dikaros
*
*/
public class Person implements Jsonable{
public Person(String name, String gender, int age, String cityName) {
this.name = name;
this.gender = gender;
this.age = age;
this.cityName = cityName;
}
public Person() {
}
@JsonParam(name="name")
String name;
@JsonParam(name="gender")
String gender;
@JsonParam(name="age")
int age;
@JsonParam(name="cityName")
String cityName;
@JsonListParam(name="friends",contentClassName=Person.class,classType = TYPE.LIST)
List friend;
@JsonListParam(name="phones",contentClassName=String.class,classType = TYPE.ARRAY)
String [] phones;
/**
* @return name
*/
public String getName() {
return name;
}
/**
* @param name 要设置的 name
*/
public void setName(String name) {
this.name = name;
}
/**
* @return gender
*/
public String getGender() {
return gender;
}
/**
* @param gender 要设置的 gender
*/
public void setGender(String gender) {
this.gender = gender;
}
/**
* @return cityName
*/
public String getCityName() {
return cityName;
}
/**
* @param cityName 要设置的 cityName
*/
public void setCityName(String cityName) {
this.cityName = cityName;
}
/**
* @return age
*/
public int getAge() {
return age;
}
/**
* @param age 要设置的 age
*/
public void setAge(int age) {
this.age = age;
}
/**
* @return friend
*/
public List getFriend() {
return friend;
}
/**
* @param friend 要设置的 friend
*/
public void setFriend(List friend) {
this.friend = friend;
}
/* (非 Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Person [name=" + name + ", gender=" + gender + ", age=" + age
+ ", cityName=" + cityName + ", friend=" + friend + ", phones="
+ Arrays.toString(phones) + "]";
}
/**
* @return phones
*/
public String[] getPhones() {
return phones;
}
/**
* @param phones 要设置的 phones
*/
public void setPhones(String[] phones) {
this.phones = phones;
}
}
package dikaros.example;
import java.util.ArrayList;
import dikaros.json.JsonExtendedUtil;
public class Example {
public static void main(String[] args) throws Exception {
Person person = new Person("老王", "男", 20, "长沙");
String[] phones ={"13000000000","14000000000"};
person.setPhones(phones);
ArrayList friends = new ArrayList<>();
friends.add(new Person("A妹子", "女", 19, "江西"));
friends.add(new Person("B妹子", "女", 20, "湖北"));
person.setFriend(friends);
String jsonFile = JsonExtendedUtil.generateJson(person);
System.out.println(jsonFile);
Person person2 = (Person) JsonExtendedUtil.compileJson(Person.class, jsonFile);
System.out.println(person2);
}
}
[{"name":"老王","gender":"男","age":20,"cityName":"长沙","friends":[{"name":"A妹纸","gender":"女","age":19,"cityName":"江西"},{"name":"B妹纸","gender":"女","age":20,"cityName":"湖北"}],"phones":["13000000000","14000000000"]}]
Person [name=老王, gender=男, age=20, cityName=长沙, friend=[Person [name=A妹纸, gender=女, age=19, cityName=江西, friend=null, phones=null], Person [name=B妹纸, gender=女, age=20, cityName=湖北, friend=null, phones=null]], phones=[13000000000,14000000000]]