用反射完成Json解析

前言:

Json解析工具已经有一大堆一大堆得了,为什么还要多此一举,自己写一个出来呢?

主要的原因是:大量的Json解析工具都不检查重复引用。一旦出现A中包含B,B中包含A这样的情况(在工作中是经常出现的!)就会崩溃!

这个工具类首先完成了对重复性的检查。除此之外呢,就是对我个人来说的额外原因了:容易扩展。

毕竟自己写的代码,想咋扩咋扩,比起读那些工具类的源码真是轻松太多了,让解析效率见鬼去吧。

<div>
</div>package regex;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import TBZ.util.date.JDate;

public class BeanTranser {

	public BeanTranser() {

	}

	// 无法正常取值时的处理方式
	public static final int SET_NULL = 0;
	public static final int SET_QUOTE = 1;

	/**
	 * 将一个对象解析为Json字符串
	 * 如果遇到两个对象间彼此引用的情况,暂不支持指定重复解析次数,一律放弃第二次解析
	 * 
	 * @param bean
	 *            解析原型
	 * @param unInstanceMode
	 *            是否检查方法名重复,为1时检查
	 * 
	 * @return 完整解析的Json串
	 * 
	 * @author Jassor
	 * @since 版本:1.2
	 */
	public String transBean(Object bean, int unInstanceMode)
			throws IllegalArgumentException, IllegalAccessException,
		SecurityException {
		// 实际执行交付给trans私用函数
		return this.trans(bean, unInstanceMode);
	}

	// 解析行径的实际执行部分
	private String trans(Object bean, int unInstanceMode) throws IllegalArgumentException,
			IllegalAccessException, SecurityException {

		// 正文
		String json = "";

		// 先检查null
		if (bean == null) {
			return null;
		}
		
		Class c = bean.getClass();

		// if(c.isPrimitive()) { // c.isPrimitive判断当前类型是不是基本类型.但是它永远不会被判断到
		// t2 = t; // 因为基本类型被拿进来时候都会变成对应的封装类型
		// }

		if (bean instanceof Number || bean instanceof Character
				|| bean instanceof Boolean) { // 所以只能用这个来判断
			json = bean.toString();
		} else if (c.equals(String.class)) { // String 特别对待
			json = RegexUtil.contain(bean.toString());
		} else if (bean instanceof Class) { // Class 特别对待
			json = null;
		} else if (bean instanceof Date) { // Date 特别对待
			json = RegexUtil.contain(JDate.DateToString((Date)bean));
		} else {
			// 再检查这个对象是否和某个之前克隆过的对象是同一个对象 基本类型不需要这个判断
			if(!checkClone(bean)) {
				return null;
			}
			// 对于已经解析过的元素,将解析的次数存进去
			setClonedObject(bean, 1);
			
			if (c.isArray()) { // 否则如果 bean 是数组
				// 取得数组的元素的类型
				Class arr = c.getComponentType();
				// t 的长度
				int length = Array.getLength(bean);
				
				if (arr.isPrimitive()) { // 如果数组是一个基本类型数组
					if(length>0) {
						json = "[";
						for (int i = 0; i < length; i++) {
							// 解析
							json += Array.get(bean, i)+",";
						}
						json = json.substring(0, json.length()-1)+"]";
					} else {
						json = "[]";
					}
				} else { // 不是基本类型数组,那就得递归
					if(length>0) {
						json = "[";
						for (int i = 0; i < length; i++) {
							// 解析
							json += this.trans(Array.get(bean, i), unInstanceMode)+",";
						}
						json = json.substring(0, json.length()-1)+"]";
					} else {
						json = "[]";
					}
				}
			} else if(bean instanceof Collection) {//list,set等视为数组
				Collection coll = (Collection)bean;
				if(!coll.isEmpty()) {
					json = "[";
					for (Object obj : coll) {
						// 解析
						json += this.trans(obj, unInstanceMode)+",";
					}
					json = json.substring(0, json.length()-1)+"]";
				} else {
					json = "[]";
				}
			} else if(bean instanceof Map) {//Map等视为对象
				Map map = (Map)bean;
				if(!map.isEmpty()) {
					json = "{";
					for (Object obj : map.keySet()) {
						// 解析
						String va = trans(map.get(obj), unInstanceMode);
						if(va != null) {
							json += obj.toString() + ":" + va + ",";
						}
					}
					json = json.substring(0, json.length()-1)+"}";
				} else {
					json = "{}";
				}
			} else { // 最后就是常规引用类型了
				
				//通过方法名来查取
				try {
					json = "{";
					//避免方法重名现象发生
					Set<String> methodNameSet = null;
					if(unInstanceMode==1)
						methodNameSet = new HashSet<String>();
					for (Method method : c.getMethods()) {
						/*
						 * Interface {
						 * 		public Number getNum();
						 * }
						 * Class extends Interface{
						 * 		public Integer getNum();
						 * }
						 * 如上结构中就会发生方法重名。
						 */
						if(unInstanceMode==1)
							if(methodNameSet.contains(method.getName())) continue;
							//问题出现在方法重载过程中
							else methodNameSet.add(method.getName());
						if(!isGetter(method)) {
							continue;
						}
						// ------- 记录属性信息------//
						String thisName = this.thisName[deeps];
						setField(thisName);
						// 遍历属性,并且对每个属性调用自身即可
						try {
							String va = this.trans(method.invoke(bean), unInstanceMode);
							if(va != null) {
								json += thisName + ":" + va + ",";
							}
						} catch (InvocationTargetException e) {
							setString();
							e.printStackTrace();
						} finally {
							backField();
						}
					}
					if(json == "{") {
						json = "{}";
					} else {
						json = json.substring(0, json.length()-1)+"}";
					}
				} catch (IllegalAccessException e) { // 这里执行的操作与下面相同,不过...
					json = "{}";
					// ------- 如果发生异常,记录异常信息------//
					setString();
					
				}															
			}
		}
		/*
		 * 好像1.8兼容性非常强,如上代码无论是Class还是final static 都不会出错
		 */
		return json;
	}

	private static int deeps = 0; // 当前操作的深度
	private static List<String> all = new ArrayList<String>(); // 当前操作的完整属性名路径(不含this)
	private static List<String> fails = new ArrayList<String>(); // 这个用于储存未能复刻的对象的信息

	// 每出现一个属性无法被复刻时执行这个
	private void setString() {
		String fff = "this";
		for (String s : all) {
			fff += "-" + s;
		}
		fails.add(fff);
	}

	// 每开始解释一个属性时执行这个
	private void setField(String name) {
		deeps++;
		all.add(name);
	}

	// 每结束解释一个属性时执行这个
	private void backField() {
		deeps--;
		if (deeps >= 0) {
			all.remove(deeps);
		}
	}
	
	private Object theClonedObject = null;
	// 采用IdentityHashMap而不是HashMap,因为它是比较内存地址是否相同来决定key是否重复的,这更符合需求
	private Map<Object, Object> clonedObject = new IdentityHashMap<Object, Object>();
	// 每次决定复刻对象时执行这个 判断是否需要复刻
	private boolean checkClone(Object o) {
		 // 传进来的o是原型中存在的某个对象 
		boolean flag = true;
		theClonedObject = clonedObject.get(o);
		if(theClonedObject != null) {
			flag = false;
		}
		return flag;
	}
	
	// 每次复刻完对象时执行这个 将复刻过的添加到列表里
	private void setClonedObject(Object key, Object value) {
		clonedObject.put(key, value);
	}
	

	// 用户复刻结束时执行这个 取得失败信息
	private String[] getThem() {
		String[] ss = new String[fails.size()];
		for (int i = 0; i < fails.size(); i++) {
			ss[i] = fails.get(i);
		}
		return ss;
	}

	// 用户复刻结束时执行这个 重置静态记录
	private void resetThem() {
		deeps = 0;
		all.clear();
		fails.clear();
		theClonedObject = null;
		clonedObject.clear();
	}

	private String[] thisName = new String[128];
	
	// 判断此方法是否是一个getter方法
	private boolean isGetter (Method method) {
		String thisName = null;
		if(method.getDeclaringClass() == Object.class) 
			return false;
		//getter不能有参数
		if(method.getParameterTypes().length != 0) {
			return false;
		}
		//getter的前缀可能是is或者get,在此进行处理
		if(method.getReturnType().equals(boolean.class)) {
			if(method.getName().startsWith("is")) {
				thisName = method.getName().substring(2);
			} else return false;
		} else {
			if(method.getName().startsWith("get")) {
				thisName = method.getName().substring(3);
			} else return false;
		}
		//对方法名部分进行剖析
		switch(thisName.length()) {
		case 0:return false;
		case 1:
			if(thisName.charAt(0)<'a') {
				thisName = thisName.toUpperCase();
				break;
			} else {
				return false;
			}
		default:
			if(thisName.charAt(1)<'a') { 
				thisName = thisName;
				return true;
			} else if(thisName.charAt(0)<'a') {
				thisName = thisName.substring(0,1).toLowerCase()+thisName.substring(1);
				break;
			} else {
				return false;
			}
		}
		this.thisName[deeps] = thisName;
		return true;
	}
	
}

用法简述:

String json = new BeanTranser().transBean(yourEntity, BeanTranser.SET_QUOTE);

依赖工具中的方法:

<span style="white-space:pre">	</span>/**
	 * 生成Json时保护其中的内容
	 * @param content
	 * @return
	 */
	public static String contain(String content) {
		if(content == null)
			content = "";
		return "\""+content.replace("\\","\\\\").replace("\"", "\\\"")+"\"";
	}
<span style="white-space:pre">	</span><pre name="code" class="java"><span style="white-space:pre">	</span>/**
<span>	</span> * 按照默认格式返回日期数据
<span>	</span> */<span>	</span>
public static String DateToString(Date date) {
return DateToString(date, "yyyy-MM-dd");
}

<span style="white-space:pre">	</span>/**
<span>	</span>* 按照给定格式format返回日期数据
<span style="white-space:pre">	</span>*/
public static String DateToString(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
String str = "";
if (date != null)
str = sdf.format(date);
else
return null;
return str;
}

 

都是以前积累的个人工具类,想必大家有比我更好的实现,如有错漏,欢迎斧正

后记:

只需要一本API手册,一个JDK1.6以上,一个免费的eclipse,制作工具类就是这么简单。

太多时候我们都在死抠网上荡来的工具类,可是假如他不为我们开通这条路,我们就打道回府吗?

诚然,我们自己做的太多东西都是别人做过的,然而“编程之久,除了算法和数据结构,什么都没剩下”,假如自己不去写这些东西,那么算法,数据结构,又从哪来呢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值