前言:
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,制作工具类就是这么简单。
太多时候我们都在死抠网上荡来的工具类,可是假如他不为我们开通这条路,我们就打道回府吗?
诚然,我们自己做的太多东西都是别人做过的,然而“编程之久,除了算法和数据结构,什么都没剩下”,假如自己不去写这些东西,那么算法,数据结构,又从哪来呢。