自己编写的一个Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加载对象[修订081217]
2008-01-07 17:21
1176 查看
最后更新:09-02-08
bug fix:支持了Hibernate one-to-many映射到List的处理
由于Json自己的String转换有问题,无法正确的转换中文为uxxxx的字符,因此改用DWR包内的JavascriptUtil处理String类型。可以直接引用,还可以拆离出来,因为仅是一个转换工具类,跟DWR的没有依赖关系。这样就可以采用ISO-8859-1编码来传递所有UNICODE了。
代码:
package com.aherp.framework.util;
import org.json.JSONString;
public class JSONStringObject implements JSONString {
private String jsonString = null;
public JSONStringObject(String jsonString){
this.jsonString = jsonString;
}
@Override
public String toString() {
return jsonString;
}
public String toJSONString() {
return jsonString;
}
}
package com.aherp.framework.util;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONString;
/**
* JSON工具类,反射的方式转换整个对象
* @author Jim Wu
*
*/
public class JSONUtil {
private static JSONUtil instance = null;
public JSONUtil(){}
/**
* 代理类是否输出的检查,返回true则允许继续转换
* @param bean
* @return 是否允许继续转换
*/
protected boolean canProxyOutput(Object bean) {
return true;
}
/**
* 代理类时做的检查.返回应该检查的对象.
* @param bean
* @return 实际获取的对象
*/
protected Object proxyConvert(Object bean){
return bean;
}
static public String toJSONString(Object obj) throws JSONException{
return toJSONString(obj, false);
}
static public String toJSONString(Object obj, boolean useClassConvert) throws JSONException{
if(instance == null)
instance = new JSONUtil();
return instance.getJSONObject(obj, useClassConvert).toString();
}
@SuppressWarnings("unchecked")
private String getJSONArray(Object arrayObj, boolean useClassConvert) throws JSONException{
if(arrayObj == null)
return "null";
arrayObj = proxyConvert(arrayObj);
JSONArray jSONArray = new JSONArray();
if(arrayObj instanceof Iterable){
for(Object rowObj: ((Iterable)arrayObj)){
if(canProxyOutput(rowObj)){
if(rowObj == null)
jSONArray.put(new JSONStringObject(null));
else if(rowObj.getClass().isArray() || rowObj instanceof Iterable)
jSONArray.put(getJSONArray(rowObj, useClassConvert));
else
jSONArray.put(getJSONObject(rowObj, useClassConvert));
}
}
}
if(arrayObj.getClass().isArray()){
int arrayLength = Array.getLength(arrayObj);
for(int i = 0; i < arrayLength; i ++){
Object rowObj = Array.get(arrayObj, i);
if(canProxyOutput(rowObj)){
if(rowObj == null)
jSONArray.put(new JSONStringObject(null));
else if(rowObj.getClass().isArray() || rowObj instanceof Iterable)
jSONArray.put(getJSONArray(rowObj, useClassConvert));
else
jSONArray.put(getJSONObject(rowObj, useClassConvert));
}
}
}
return jSONArray.toString();
}
@SuppressWarnings("unchecked")
JSONStringObject getJSONObject(Object value, boolean useClassConvert) throws JSONException{
//处理原始类型
if (value == null) {
return new JSONStringObject("null");
}
value = proxyConvert(value);
if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString)value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
throw new JSONException("Bad value from toJSONString: " + o);
}
if (value instanceof Number) {
return new JSONStringObject(JSONObject.numberToString((Number) value));
}
if (value instanceof Boolean || value instanceof JSONObject ||
value instanceof JSONArray) {
return new JSONStringObject(value.toString());
}
if (value instanceof String)
return new JSONStringObject('"' + JavascriptUtil.escapeJavaScript(value.toString()) + '"');
if (value instanceof Map) {
JSONObject jSONObject = new JSONObject();
for(Object rowObj: ((Map)value).entrySet()){
Entry entry = (Entry)rowObj;
Object valueObj = entry.getValue();
if(canProxyOutput(valueObj))
jSONObject.put(entry.getKey().toString(), getJSONObject(valueObj, useClassConvert));
}
return new JSONStringObject(jSONObject.toString());
}
if(value instanceof Date){
Calendar calendar = Calendar.getInstance();
calendar.setTime(calendar.getTime());
StringBuilder sb = new StringBuilder("new Date(");
sb.append(calendar.get(Calendar.YEAR));
sb.append(",");
sb.append(calendar.get(Calendar.MONTH));
sb.append(",");
sb.append(calendar.get(Calendar.DAY_OF_MONTH));
sb.append(",");
sb.append(calendar.get(Calendar.HOUR_OF_DAY));
sb.append(",");
sb.append(calendar.get(Calendar.MINUTE));
sb.append(",");
sb.append(calendar.get(Calendar.SECOND));
sb.append(")");
return new JSONStringObject(sb.toString());
}
//class
if(value instanceof Class)
return new JSONStringObject(JavascriptUtil.escapeJavaScript(((Class)value).getSimpleName()));
//数组
if (value instanceof Iterable || value.getClass().isArray()) {
return new JSONStringObject(getJSONArray(proxyConvert(value), useClassConvert));
}
return reflectObject(value, useClassConvert);
}
@SuppressWarnings("unchecked")
private JSONStringObject reflectObject(Object bean, boolean useClassConvert){
JSONObject jSONObject = new JSONObject();
Class klass = bean.getClass();
for (Method method: klass.getMethods()) {
try {
String name = method.getName();
String key = "";
if (name.startsWith("get")) {
key = name.substring(3);
} else if (name.startsWith("is")) {
key = name.substring(2);
}
if (key.length() > 0 &
Character.isUpperCase(key.charAt(0)) &
method.getParameterTypes().length == 0) {
if (key.length() == 1) {
key = key.toLowerCase();
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase() +
key.substring(1);
}
Object elementObj = method.invoke(bean);
if(!useClassConvert && elementObj instanceof Class)
continue;
if(canProxyOutput(elementObj))
jSONObject.put(key, getJSONObject(elementObj, useClassConvert));
}
} catch (Exception e) {
/* forget about it */
}
}
return new JSONStringObject(jSONObject.toString());
}
}
调用测试程序
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class AObj ...{
private int ii = 7;
private boolean bb = true;
private String ss = "你好";
private List aList = new ArrayList();
public AObj()...{
aList.add("hello");
aList.add(false);
aList.add(new BObj());
aList.add(new HashMap());
}
public boolean isBb() ...{
return bb;
}
public void setBb(boolean bb) ...{
this.bb = bb;
}
public int getIi() ...{
return ii;
}
public void setIi(int ii) ...{
this.ii = ii;
}
public String getSs() ...{
return ss;
}
public void setSs(String ss) ...{
this.ss = ss;
}
public List getAList() ...{
return aList;
}
public void setAList(List list) ...{
aList = list;
}
}
import java.math.BigDecimal;
import java.util.HashMap;
public class BObj ...{
private HashMap innerhm = new HashMap();
public BObj()...{
double dd = 7.4354;
innerhm.put("gigi", "高兴");
innerhm.put("sina", new BigDecimal(dd));
}
public HashMap getInnerhm() ...{
return innerhm;
}
public void setInnerhm(HashMap innerhm) ...{
this.innerhm = innerhm;
}
}
public class CObj extends AObj...{
private Object[] oarray = new Object[]...{352, false, "kick"};
public Object[] getOarray() ...{
return oarray;
}
public void setOarray(Object[] oarray) ...{
this.oarray = oarray;
}
}
import org.json.JSONException;
import com.aherp.framework.util.JSONUtil;
public class Test ...{
public static void main(String[] args) throws JSONException ...{
CObj cObj = new CObj();
System.out.println(JSONUtil.toJSONString(cObj));
}
}
输出:
{"AList":["hello",false,{"innerhm":{"gigi":"/u9AD8/u5174","sina":7.4353999999999995651478457148186862468719482421875}},{}],"ii":7,"oarray":[352,false,"kick"],"ss":"/u4F60/u597D","bb":true}
如果需要支持Hibernate,那么必须弄清其机制。Hibernate采用CGLIB对VO对象进行字节码增加,实际机制就是使用一个原类型的proxy子类,其子类实现了HibernateProxy接口。其接口有一个isUninitialized的判断方法,用来判断该代理类是否已经初始化(还记得在事务外使用延迟加载的对象会抛no Session的错误吗,正是由于实际使用的对象已经变成原来类的子类proxy了)。而对于one-to-many映射时,很难判断对象只加载一次,因此为了避免递归调用死循环,忽略了Hibernate的one-to-many集合的递归反射。其原理和many-to-one一样,也是一个子类化的proxy,具有PersistentSet的接口。
因此,支持Hibernate的JSONUtil如下:
package com.aherp.framework.util;
import org.hibernate.collection.PersistentSet;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.json.JSONException;
/**
* 支持Hibernate的JSONUtil.
* 自动检测是否已经代理加载,如果未加载,则将对象仅加载为OID
* @author Jim Wu
*
*/
public class HiJSONUtil extends JSONUtil {
private static HiJSONUtil instance = null;
static public String toJSONString(Object obj) throws JSONException{
return toJSONString(obj, false);
}
static public String toJSONString(Object obj, boolean useClassConvert) throws JSONException{
if(instance == null)
instance = new HiJSONUtil();
return instance.getJSONObject(obj, useClassConvert).toString();
}
@Override
protected Object proxyConvert(Object bean) {
if(bean instanceof HibernateProxy){
LazyInitializer lazyInitializer = ((HibernateProxy)bean).getHibernateLazyInitializer();
if(lazyInitializer.isUninitialized()){
return lazyInitializer.getIdentifier();
}else
return lazyInitializer.getImplementation();
}
if(bean instanceof PersistentSet || bean instanceof PersistentList){
return new String[]{}; //忽略hibernate one-to-many
}
return bean;
}
@Override
protected boolean canProxyOutput(Object bean) {
return !(bean != null && (bean instanceof PersistentSet || bean instanceof PersistentList));
}
}
;
但是这样还是有个问题,当one-to-one具备双向映射关系时,会陷入调用递归死循环。因此避免这样的情况。
将本人修改过的JavaScriptUtil也附上
/*
* Copyright 2005 Joe Walker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.aherp.framework.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Various Javascript code utilities.
* The escape classes were taken from jakarta-commons-lang which in turn borrowed
* from Turbine and other projects. The list of authors below is almost certainly
* far too long, but I'm not sure who really wrote these methods.
* @author Joe Walker [joe at getahead dot ltd dot uk]
* @author Apache Jakarta Turbine
* @author GenerationJavaCore library
* @author Purple Technology
* @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
* @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
* @author <a href="mailto:cybertiger@cyberiantiger.org">Antony Riley</a>
* @author Helge Tesgaard
* @author <a href="sean@boohai.com">Sean Brown</a>
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Phil Steitz
* @author Pete Gieser
*/
@SuppressWarnings("unchecked")
public class JavascriptUtil
{
/**
* Flag for use in javascript compression: Remove single line comments.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
* @noinspection PointlessBitwiseExpression
*/
public static final int COMPRESS_STRIP_SL_COMMENTS = 1 << 0;
/**
* Flag for use in javascript compression: Remove multi line comments.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_STRIP_ML_COMMENTS = 1 << 1;
/**
* Flag for use in javascript compression: Remove whitespace at the start and end of a line.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_TRIM_LINES = 1 << 2;
/**
* Flag for use in javascript compression: Remove blank lines.
* This option will make the javascript harder to debug because line number references
* are likely be altered.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_STRIP_BLANKLINES = 1 << 3;
/**
* Flag for use in javascript compression: Shrink variable names.
* This option is currently un-implemented.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_SHRINK_VARS = 1 << 4;
/**
* Flag for use in javascript compression: Remove all lines endings.
* Warning: Javascript can add semi-colons in for you. If you make use of this feature
* then removing newlines may well break.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_REMOVE_NEWLINES = 1 << 5;
/**
* Compression level that leaves the source un-touched.
*/
public static final int LEVEL_NONE = 0;
/**
* Basic compression that leaves the source fully debuggable.
* This includes removing all comments and extraneous whitespace.
*/
public static final int LEVEL_DEBUGGABLE = COMPRESS_STRIP_SL_COMMENTS | COMPRESS_STRIP_ML_COMMENTS | COMPRESS_TRIM_LINES;
/**
* Normal compression makes all changes that will work for generic javascript.
* This adds variable name compression and blank line removal in addition to the
* compressions done by LEVEL_DEBUGGABLE.
*/
public static final int LEVEL_NORMAL = LEVEL_DEBUGGABLE | COMPRESS_STRIP_BLANKLINES | COMPRESS_SHRINK_VARS;
/**
* LEVEL_ULTRA performs additional compression that makes some assumptions about the
* style of javascript.
* Specifically it assumes that you are not using javascripts ability to infer where the ;
* should go.
*/
public static final int LEVEL_ULTRA = LEVEL_NORMAL | COMPRESS_REMOVE_NEWLINES;
/**
* Compress the source code by removing java style comments and removing
* leading and trailing spaces.
* @param text The javascript (or java) program to compress
* @param level The compression level - see LEVEL_* and COMPRESS_* constants.
* @return The compressed version
*/
public static String compress(String text, int level)
{
String reply = text;
// First we strip multi line comments. I think this is important:
if ((level & COMPRESS_STRIP_ML_COMMENTS) != 0)
{
reply = stripMultiLineComments(text);
}
if ((level & COMPRESS_STRIP_SL_COMMENTS) != 0)
{
reply = stripSingleLineComments(reply);
}
if ((level & COMPRESS_TRIM_LINES) != 0)
{
reply = trimLines(reply);
}
if ((level & COMPRESS_STRIP_BLANKLINES) != 0)
{
reply = stripBlankLines(reply);
}
if ((level & COMPRESS_SHRINK_VARS) != 0)
{
reply = shrinkVariableNames(reply);
}
if ((level & COMPRESS_REMOVE_NEWLINES) != 0)
{
reply = stripNewlines(reply);
}
return reply;
}
/**
* Remove any leading or trailing spaces from a line of code.
* This function could be improved by making it strip unnecessary double
* spaces, but since we would need to leave double spaces inside strings
* this is not simple and since the benefit is small, we'll leave it for now
* @param text The javascript program to strip spaces from.
* @return The stripped program
*/
public static String trimLines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
// First we strip multi line comments. I think this is important:
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
output.append(line.trim());
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all the single-line comments from a block of text
* @param text The text to remove single-line comments from
* @return The single-line comment free text
*/
public static String stripSingleLineComments(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
// Skip @DWR comments
if (line.indexOf(COMMENT_RETAIN) == -1)
{
int cstart = line.indexOf(COMMENT_SL_START);
if (cstart >= 0)
{
line = line.substring(0, cstart);
}
}
output.append(line);
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all the multi-line comments from a block of text
* @param text The text to remove multi-line comments from
* @return The multi-line comment free text
*/
public static String stripMultiLineComments(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
// Comment rules:
/*/ This is still a comment
/* /* */ // Comments do not nest
// /* */ This is in a comment
/* // */ // The second // is needed to make this a comment.
// First we strip multi line comments. I think this is important:
boolean inMultiLine = false;
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
if (!inMultiLine)
{
// We are not in a multi-line comment, check for a start
int cstart = line.indexOf(COMMENT_ML_START);
if (cstart >= 0)
{
// This could be a MLC on one line ...
int cend = line.indexOf(COMMENT_ML_END, cstart + COMMENT_ML_START.length());
if (cend >= 0)
{
// A comment that starts and ends on one line
// BUG: you can have more than 1 multi-line comment on a line
line = line.substring(0, cstart) + SPACE + line.substring(cend + COMMENT_ML_END.length());
}
else
{
// A real multi-line comment
inMultiLine = true;
line = line.substring(0, cstart) + SPACE;
}
}
else
{
// We are not in a multi line comment and we havn't
// started one so we are going to ignore closing
// comments even if they exist.
}
}
else
{
// We are in a multi-line comment, check for the end
int cend = line.indexOf(COMMENT_ML_END);
if (cend >= 0)
{
// End of comment
line = line.substring(cend + COMMENT_ML_END.length());
inMultiLine = false;
}
else
{
// The comment continues
line = SPACE;
}
}
output.append(line);
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all blank lines from a string.
* A blank line is defined to be a line where the only characters are whitespace.
* We always ensure that the line contains a newline at the end.
* @param text The string to strip blank lines from
* @return The blank line stripped reply
*/
public static String stripBlankLines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
boolean doneOneLine = false;
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
if (line.trim().length() > 0)
{
output.append(line);
output.append('/n');
doneOneLine = true;
}
}
if (!doneOneLine)
{
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all newline characters from a string.
* @param text The string to strip newline characters from
* @return The stripped reply
*/
public static String stripNewlines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
output.append(line);
output.append(SPACE);
}
output.append('/n');
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Shrink variable names to a minimum.
* @param text The javascript program to shrink the variable names in.
* @return The shrunk version of the javascript program.
*/
public static String shrinkVariableNames(String text)
{
if (text == null)
{
return null;
}
throw new UnsupportedOperationException("Variable name shrinking is not supported");
}
/**
* <p>Escapes the characters in a <code>String</code> using JavaScript String rules.</p>
* <p>Escapes any values it finds into their JavaScript String form.
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* <p>So a tab becomes the characters <code>'//'</code> and
* <code>'t'</code>.</p>
*
* <p>The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote must be escaped.</p>
*
* <p>Example:
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn/'t say, /"Stop!/"
* </pre>
* </p>
*
* @param str String to escape values in, may be null
* @return String with escaped values, <code>null</code> if null string input
*/
public static String escapeJavaScript(String str)
{
if (str == null)
{
return null;
}
StringBuffer writer = new StringBuffer(str.length() * 2);
int sz = str.length();
for (int i = 0; i < sz; i++)
{
char ch = str.charAt(i);
// handle unicode
if (ch > 0xfff)
{
writer.append("//u");
writer.append(hex(ch));
}
else if (ch > 0xff)
{
writer.append("//u0");
writer.append(hex(ch));
}
else if (ch > 0x7f)
{
writer.append("//u00");
writer.append(hex(ch));
}
else if (ch < 32)
{
switch (ch)
{
case '/b':
writer.append('//');
writer.append('b');
break;
case '/n':
writer.append('//');
writer.append('n');
break;
case '/t':
writer.append('//');
writer.append('t');
break;
case '/f':
writer.append('//');
writer.append('f');
break;
case '/r':
writer.append('//');
writer.append('r');
break;
default:
if (ch > 0xf)
{
writer.append("//u00");
writer.append(hex(ch));
}
else
{
writer.append("//u000");
writer.append(hex(ch));
}
break;
}
}
else
{
switch (ch)
{
case '/'':
// If we wanted to escape for Java strings then we would
// not need this next line.
writer.append('//');
writer.append('/'');
break;
case '"':
writer.append('//');
writer.append('"');
break;
case '//':
writer.append('//');
writer.append('//');
break;
case '<':
writer.append("//u003c");
break;
case '>':
writer.append("//u003e");
break;
default:
writer.append(ch);
break;
}
}
}
return writer.toString();
}
/**
* <p>Returns an upper case hexadecimal <code>String</code> for the given
* character.</p>
* @param ch The character to convert.
* @return An upper case hexadecimal <code>String</code>
*/
private static String hex(char ch)
{
return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
}
/**
* <p>Unescapes any JavaScript literals found in the <code>String</code>.</p>
* <p>For example, it will turn a sequence of <code>'/'</code> and <code>'n'</code>
* into a newline character, unless the <code>'/'</code> is preceded by another
* <code>'/'</code>.</p>
* @param str the <code>String</code> to unescape, may be null
* @return A new unescaped <code>String</code>, <code>null</code> if null string input
*/
public static String unescapeJavaScript(String str)
{
if (str == null)
{
return null;
}
StringBuffer writer = new StringBuffer(str.length());
int sz = str.length();
StringBuffer unicode = new StringBuffer(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0; i < sz; i++)
{
char ch = str.charAt(i);
if (inUnicode)
{
// if in unicode, then we're reading unicode
// values in somehow
unicode.append(ch);
if (unicode.length() == 4)
{
// unicode now contains the four hex digits
// which represents our unicode chacater
try
{
int value = Integer.parseInt(unicode.toString(), 16);
writer.append((char) value);
unicode.setLength(0);
inUnicode = false;
hadSlash = false;
}
catch (NumberFormatException nfe)
{
throw new IllegalArgumentException("Unable to parse unicode value: " + unicode + " cause: " + nfe);
}
}
continue;
}
if (hadSlash)
{
// handle an escaped value
hadSlash = false;
switch (ch)
{
case '//':
writer.append('//');
break;
case '/'':
writer.append('/'');
break;
case '/"':
writer.append('"');
break;
case 'r':
writer.append('/r');
break;
case 'f':
writer.append('/f');
break;
case 't':
writer.append('/t');
break;
case 'n':
writer.append('/n');
break;
case 'b':
writer.append('/b');
break;
case 'u':
// uh-oh, we're in unicode country....
inUnicode = true;
break;
default:
writer.append(ch);
break;
}
continue;
}
else if (ch == '//')
{
hadSlash = true;
continue;
}
writer.append(ch);
}
if (hadSlash)
{
// then we're in the weird case of a / at the end of the
// string, let's output it anyway.
writer.append('//');
}
return writer.toString();
}
/**
* Check to see if the given word is reserved or a bad idea in any known
* version of JavaScript.
* @param name The word to check
* @return false if the word is not reserved
*/
public static boolean isReservedWord(String name)
{
return reserved.contains(name);
}
/**
* The array of javascript reserved words
*/
private static final String[] RESERVED_ARRAY = new String[]
{
// Reserved and used at ECMAScript 4
"as",
"break",
"case",
"catch",
"class",
"const",
"continue",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"is",
"namespace",
"new",
"null",
"package",
"private",
"public",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"use",
"var",
"void",
"while",
"with",
// Reserved for future use at ECMAScript 4
"abstract",
"debugger",
"enum",
"goto",
"implements",
"interface",
"native",
"protected",
"synchronized",
"throws",
"transient",
"volatile",
// Reserved in ECMAScript 3, unreserved at 4 best to avoid anyway
"boolean",
"byte",
"char",
"double",
"final",
"float",
"int",
"long",
"short",
"static",
// I have seen the folowing list as 'best avoided for function names'
// but it seems way to all encompassing, so I'm not going to include it
/*
"alert", "anchor", "area", "arguments", "array", "assign", "blur",
"boolean", "button", "callee", "caller", "captureevents", "checkbox",
"clearinterval", "cleartimeout", "close", "closed", "confirm",
"constructor", "date", "defaultstatus", "document", "element", "escape",
"eval", "fileupload", "find", "focus", "form", "frame", "frames",
"getclass", "hidden", "history", "home", "image", "infinity",
"innerheight", "isfinite", "innerwidth", "isnan", "java", "javaarray",
"javaclass", "javaobject", "javapackage", "length", "link", "location",
"locationbar", "math", "menubar", "mimetype", "moveby", "moveto",
"name", "nan", "navigate", "navigator", "netscape", "number", "object",
"onblur", "onerror", "onfocus", "onload", "onunload", "open", "opener",
"option", "outerheight", "outerwidth", "packages", "pagexoffset",
"pageyoffset", "parent", "parsefloat", "parseint", "password",
"personalbar", "plugin", "print", "prompt", "prototype", "radio", "ref",
"regexp", "releaseevents", "reset", "resizeby", "resizeto",
"routeevent", "scroll", "scrollbars", "scrollby", "scrollto", "select",
"self", "setinterval", "settimeout", "status", "statusbar", "stop",
"string", "submit", "sun", "taint", "text", "textarea", "toolbar",
"top", "tostring", "unescape", "untaint", "unwatch", "valueof", "watch",
"window",
*/
};
private static SortedSet reserved = new TreeSet();
/**
* For easy access ...
*/
static
{
// The Javascript reserved words array so we don't generate illegal javascript
reserved.addAll(Arrays.asList(RESERVED_ARRAY));
}
private static final String SPACE = " ";
/**
* How does a multi line comment start?
*/
private static final String COMMENT_ML_START = "/*";
/**
* How does a multi line comment end?
*/
private static final String COMMENT_ML_END = "*/";
/**
* How does a single line comment start?
*/
private static final String COMMENT_SL_START = "//";
/**
* Sometimes we need to retain the comment because it has special meaning
*/
private static final String COMMENT_RETAIN = "#DWR";
/**
* The log stream
*/
private static final Log log = LogFactory.getLog(JavascriptUtil.class);
}
需要的JSON类我已经放到共享,你可以从
http://download.csdn.net/source/879579
下载
bug fix:支持了Hibernate one-to-many映射到List的处理
由于Json自己的String转换有问题,无法正确的转换中文为uxxxx的字符,因此改用DWR包内的JavascriptUtil处理String类型。可以直接引用,还可以拆离出来,因为仅是一个转换工具类,跟DWR的没有依赖关系。这样就可以采用ISO-8859-1编码来传递所有UNICODE了。
代码:
package com.aherp.framework.util;
import org.json.JSONString;
public class JSONStringObject implements JSONString {
private String jsonString = null;
public JSONStringObject(String jsonString){
this.jsonString = jsonString;
}
@Override
public String toString() {
return jsonString;
}
public String toJSONString() {
return jsonString;
}
}
package com.aherp.framework.util;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONString;
/**
* JSON工具类,反射的方式转换整个对象
* @author Jim Wu
*
*/
public class JSONUtil {
private static JSONUtil instance = null;
public JSONUtil(){}
/**
* 代理类是否输出的检查,返回true则允许继续转换
* @param bean
* @return 是否允许继续转换
*/
protected boolean canProxyOutput(Object bean) {
return true;
}
/**
* 代理类时做的检查.返回应该检查的对象.
* @param bean
* @return 实际获取的对象
*/
protected Object proxyConvert(Object bean){
return bean;
}
static public String toJSONString(Object obj) throws JSONException{
return toJSONString(obj, false);
}
static public String toJSONString(Object obj, boolean useClassConvert) throws JSONException{
if(instance == null)
instance = new JSONUtil();
return instance.getJSONObject(obj, useClassConvert).toString();
}
@SuppressWarnings("unchecked")
private String getJSONArray(Object arrayObj, boolean useClassConvert) throws JSONException{
if(arrayObj == null)
return "null";
arrayObj = proxyConvert(arrayObj);
JSONArray jSONArray = new JSONArray();
if(arrayObj instanceof Iterable){
for(Object rowObj: ((Iterable)arrayObj)){
if(canProxyOutput(rowObj)){
if(rowObj == null)
jSONArray.put(new JSONStringObject(null));
else if(rowObj.getClass().isArray() || rowObj instanceof Iterable)
jSONArray.put(getJSONArray(rowObj, useClassConvert));
else
jSONArray.put(getJSONObject(rowObj, useClassConvert));
}
}
}
if(arrayObj.getClass().isArray()){
int arrayLength = Array.getLength(arrayObj);
for(int i = 0; i < arrayLength; i ++){
Object rowObj = Array.get(arrayObj, i);
if(canProxyOutput(rowObj)){
if(rowObj == null)
jSONArray.put(new JSONStringObject(null));
else if(rowObj.getClass().isArray() || rowObj instanceof Iterable)
jSONArray.put(getJSONArray(rowObj, useClassConvert));
else
jSONArray.put(getJSONObject(rowObj, useClassConvert));
}
}
}
return jSONArray.toString();
}
@SuppressWarnings("unchecked")
JSONStringObject getJSONObject(Object value, boolean useClassConvert) throws JSONException{
//处理原始类型
if (value == null) {
return new JSONStringObject("null");
}
value = proxyConvert(value);
if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString)value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
throw new JSONException("Bad value from toJSONString: " + o);
}
if (value instanceof Number) {
return new JSONStringObject(JSONObject.numberToString((Number) value));
}
if (value instanceof Boolean || value instanceof JSONObject ||
value instanceof JSONArray) {
return new JSONStringObject(value.toString());
}
if (value instanceof String)
return new JSONStringObject('"' + JavascriptUtil.escapeJavaScript(value.toString()) + '"');
if (value instanceof Map) {
JSONObject jSONObject = new JSONObject();
for(Object rowObj: ((Map)value).entrySet()){
Entry entry = (Entry)rowObj;
Object valueObj = entry.getValue();
if(canProxyOutput(valueObj))
jSONObject.put(entry.getKey().toString(), getJSONObject(valueObj, useClassConvert));
}
return new JSONStringObject(jSONObject.toString());
}
if(value instanceof Date){
Calendar calendar = Calendar.getInstance();
calendar.setTime(calendar.getTime());
StringBuilder sb = new StringBuilder("new Date(");
sb.append(calendar.get(Calendar.YEAR));
sb.append(",");
sb.append(calendar.get(Calendar.MONTH));
sb.append(",");
sb.append(calendar.get(Calendar.DAY_OF_MONTH));
sb.append(",");
sb.append(calendar.get(Calendar.HOUR_OF_DAY));
sb.append(",");
sb.append(calendar.get(Calendar.MINUTE));
sb.append(",");
sb.append(calendar.get(Calendar.SECOND));
sb.append(")");
return new JSONStringObject(sb.toString());
}
//class
if(value instanceof Class)
return new JSONStringObject(JavascriptUtil.escapeJavaScript(((Class)value).getSimpleName()));
//数组
if (value instanceof Iterable || value.getClass().isArray()) {
return new JSONStringObject(getJSONArray(proxyConvert(value), useClassConvert));
}
return reflectObject(value, useClassConvert);
}
@SuppressWarnings("unchecked")
private JSONStringObject reflectObject(Object bean, boolean useClassConvert){
JSONObject jSONObject = new JSONObject();
Class klass = bean.getClass();
for (Method method: klass.getMethods()) {
try {
String name = method.getName();
String key = "";
if (name.startsWith("get")) {
key = name.substring(3);
} else if (name.startsWith("is")) {
key = name.substring(2);
}
if (key.length() > 0 &
Character.isUpperCase(key.charAt(0)) &
method.getParameterTypes().length == 0) {
if (key.length() == 1) {
key = key.toLowerCase();
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase() +
key.substring(1);
}
Object elementObj = method.invoke(bean);
if(!useClassConvert && elementObj instanceof Class)
continue;
if(canProxyOutput(elementObj))
jSONObject.put(key, getJSONObject(elementObj, useClassConvert));
}
} catch (Exception e) {
/* forget about it */
}
}
return new JSONStringObject(jSONObject.toString());
}
}
调用测试程序
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class AObj ...{
private int ii = 7;
private boolean bb = true;
private String ss = "你好";
private List aList = new ArrayList();
public AObj()...{
aList.add("hello");
aList.add(false);
aList.add(new BObj());
aList.add(new HashMap());
}
public boolean isBb() ...{
return bb;
}
public void setBb(boolean bb) ...{
this.bb = bb;
}
public int getIi() ...{
return ii;
}
public void setIi(int ii) ...{
this.ii = ii;
}
public String getSs() ...{
return ss;
}
public void setSs(String ss) ...{
this.ss = ss;
}
public List getAList() ...{
return aList;
}
public void setAList(List list) ...{
aList = list;
}
}
import java.math.BigDecimal;
import java.util.HashMap;
public class BObj ...{
private HashMap innerhm = new HashMap();
public BObj()...{
double dd = 7.4354;
innerhm.put("gigi", "高兴");
innerhm.put("sina", new BigDecimal(dd));
}
public HashMap getInnerhm() ...{
return innerhm;
}
public void setInnerhm(HashMap innerhm) ...{
this.innerhm = innerhm;
}
}
public class CObj extends AObj...{
private Object[] oarray = new Object[]...{352, false, "kick"};
public Object[] getOarray() ...{
return oarray;
}
public void setOarray(Object[] oarray) ...{
this.oarray = oarray;
}
}
import org.json.JSONException;
import com.aherp.framework.util.JSONUtil;
public class Test ...{
public static void main(String[] args) throws JSONException ...{
CObj cObj = new CObj();
System.out.println(JSONUtil.toJSONString(cObj));
}
}
输出:
{"AList":["hello",false,{"innerhm":{"gigi":"/u9AD8/u5174","sina":7.4353999999999995651478457148186862468719482421875}},{}],"ii":7,"oarray":[352,false,"kick"],"ss":"/u4F60/u597D","bb":true}
如果需要支持Hibernate,那么必须弄清其机制。Hibernate采用CGLIB对VO对象进行字节码增加,实际机制就是使用一个原类型的proxy子类,其子类实现了HibernateProxy接口。其接口有一个isUninitialized的判断方法,用来判断该代理类是否已经初始化(还记得在事务外使用延迟加载的对象会抛no Session的错误吗,正是由于实际使用的对象已经变成原来类的子类proxy了)。而对于one-to-many映射时,很难判断对象只加载一次,因此为了避免递归调用死循环,忽略了Hibernate的one-to-many集合的递归反射。其原理和many-to-one一样,也是一个子类化的proxy,具有PersistentSet的接口。
因此,支持Hibernate的JSONUtil如下:
package com.aherp.framework.util;
import org.hibernate.collection.PersistentSet;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.json.JSONException;
/**
* 支持Hibernate的JSONUtil.
* 自动检测是否已经代理加载,如果未加载,则将对象仅加载为OID
* @author Jim Wu
*
*/
public class HiJSONUtil extends JSONUtil {
private static HiJSONUtil instance = null;
static public String toJSONString(Object obj) throws JSONException{
return toJSONString(obj, false);
}
static public String toJSONString(Object obj, boolean useClassConvert) throws JSONException{
if(instance == null)
instance = new HiJSONUtil();
return instance.getJSONObject(obj, useClassConvert).toString();
}
@Override
protected Object proxyConvert(Object bean) {
if(bean instanceof HibernateProxy){
LazyInitializer lazyInitializer = ((HibernateProxy)bean).getHibernateLazyInitializer();
if(lazyInitializer.isUninitialized()){
return lazyInitializer.getIdentifier();
}else
return lazyInitializer.getImplementation();
}
if(bean instanceof PersistentSet || bean instanceof PersistentList){
return new String[]{}; //忽略hibernate one-to-many
}
return bean;
}
@Override
protected boolean canProxyOutput(Object bean) {
return !(bean != null && (bean instanceof PersistentSet || bean instanceof PersistentList));
}
}
;
但是这样还是有个问题,当one-to-one具备双向映射关系时,会陷入调用递归死循环。因此避免这样的情况。
将本人修改过的JavaScriptUtil也附上
/*
* Copyright 2005 Joe Walker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.aherp.framework.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Various Javascript code utilities.
* The escape classes were taken from jakarta-commons-lang which in turn borrowed
* from Turbine and other projects. The list of authors below is almost certainly
* far too long, but I'm not sure who really wrote these methods.
* @author Joe Walker [joe at getahead dot ltd dot uk]
* @author Apache Jakarta Turbine
* @author GenerationJavaCore library
* @author Purple Technology
* @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
* @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
* @author <a href="mailto:cybertiger@cyberiantiger.org">Antony Riley</a>
* @author Helge Tesgaard
* @author <a href="sean@boohai.com">Sean Brown</a>
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Phil Steitz
* @author Pete Gieser
*/
@SuppressWarnings("unchecked")
public class JavascriptUtil
{
/**
* Flag for use in javascript compression: Remove single line comments.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
* @noinspection PointlessBitwiseExpression
*/
public static final int COMPRESS_STRIP_SL_COMMENTS = 1 << 0;
/**
* Flag for use in javascript compression: Remove multi line comments.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_STRIP_ML_COMMENTS = 1 << 1;
/**
* Flag for use in javascript compression: Remove whitespace at the start and end of a line.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_TRIM_LINES = 1 << 2;
/**
* Flag for use in javascript compression: Remove blank lines.
* This option will make the javascript harder to debug because line number references
* are likely be altered.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_STRIP_BLANKLINES = 1 << 3;
/**
* Flag for use in javascript compression: Shrink variable names.
* This option is currently un-implemented.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_SHRINK_VARS = 1 << 4;
/**
* Flag for use in javascript compression: Remove all lines endings.
* Warning: Javascript can add semi-colons in for you. If you make use of this feature
* then removing newlines may well break.
* For ease of use you may wish to use one of the LEVEL_* compression levels.
*/
public static final int COMPRESS_REMOVE_NEWLINES = 1 << 5;
/**
* Compression level that leaves the source un-touched.
*/
public static final int LEVEL_NONE = 0;
/**
* Basic compression that leaves the source fully debuggable.
* This includes removing all comments and extraneous whitespace.
*/
public static final int LEVEL_DEBUGGABLE = COMPRESS_STRIP_SL_COMMENTS | COMPRESS_STRIP_ML_COMMENTS | COMPRESS_TRIM_LINES;
/**
* Normal compression makes all changes that will work for generic javascript.
* This adds variable name compression and blank line removal in addition to the
* compressions done by LEVEL_DEBUGGABLE.
*/
public static final int LEVEL_NORMAL = LEVEL_DEBUGGABLE | COMPRESS_STRIP_BLANKLINES | COMPRESS_SHRINK_VARS;
/**
* LEVEL_ULTRA performs additional compression that makes some assumptions about the
* style of javascript.
* Specifically it assumes that you are not using javascripts ability to infer where the ;
* should go.
*/
public static final int LEVEL_ULTRA = LEVEL_NORMAL | COMPRESS_REMOVE_NEWLINES;
/**
* Compress the source code by removing java style comments and removing
* leading and trailing spaces.
* @param text The javascript (or java) program to compress
* @param level The compression level - see LEVEL_* and COMPRESS_* constants.
* @return The compressed version
*/
public static String compress(String text, int level)
{
String reply = text;
// First we strip multi line comments. I think this is important:
if ((level & COMPRESS_STRIP_ML_COMMENTS) != 0)
{
reply = stripMultiLineComments(text);
}
if ((level & COMPRESS_STRIP_SL_COMMENTS) != 0)
{
reply = stripSingleLineComments(reply);
}
if ((level & COMPRESS_TRIM_LINES) != 0)
{
reply = trimLines(reply);
}
if ((level & COMPRESS_STRIP_BLANKLINES) != 0)
{
reply = stripBlankLines(reply);
}
if ((level & COMPRESS_SHRINK_VARS) != 0)
{
reply = shrinkVariableNames(reply);
}
if ((level & COMPRESS_REMOVE_NEWLINES) != 0)
{
reply = stripNewlines(reply);
}
return reply;
}
/**
* Remove any leading or trailing spaces from a line of code.
* This function could be improved by making it strip unnecessary double
* spaces, but since we would need to leave double spaces inside strings
* this is not simple and since the benefit is small, we'll leave it for now
* @param text The javascript program to strip spaces from.
* @return The stripped program
*/
public static String trimLines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
// First we strip multi line comments. I think this is important:
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
output.append(line.trim());
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all the single-line comments from a block of text
* @param text The text to remove single-line comments from
* @return The single-line comment free text
*/
public static String stripSingleLineComments(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
// Skip @DWR comments
if (line.indexOf(COMMENT_RETAIN) == -1)
{
int cstart = line.indexOf(COMMENT_SL_START);
if (cstart >= 0)
{
line = line.substring(0, cstart);
}
}
output.append(line);
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all the multi-line comments from a block of text
* @param text The text to remove multi-line comments from
* @return The multi-line comment free text
*/
public static String stripMultiLineComments(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
// Comment rules:
/*/ This is still a comment
/* /* */ // Comments do not nest
// /* */ This is in a comment
/* // */ // The second // is needed to make this a comment.
// First we strip multi line comments. I think this is important:
boolean inMultiLine = false;
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
if (!inMultiLine)
{
// We are not in a multi-line comment, check for a start
int cstart = line.indexOf(COMMENT_ML_START);
if (cstart >= 0)
{
// This could be a MLC on one line ...
int cend = line.indexOf(COMMENT_ML_END, cstart + COMMENT_ML_START.length());
if (cend >= 0)
{
// A comment that starts and ends on one line
// BUG: you can have more than 1 multi-line comment on a line
line = line.substring(0, cstart) + SPACE + line.substring(cend + COMMENT_ML_END.length());
}
else
{
// A real multi-line comment
inMultiLine = true;
line = line.substring(0, cstart) + SPACE;
}
}
else
{
// We are not in a multi line comment and we havn't
// started one so we are going to ignore closing
// comments even if they exist.
}
}
else
{
// We are in a multi-line comment, check for the end
int cend = line.indexOf(COMMENT_ML_END);
if (cend >= 0)
{
// End of comment
line = line.substring(cend + COMMENT_ML_END.length());
inMultiLine = false;
}
else
{
// The comment continues
line = SPACE;
}
}
output.append(line);
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all blank lines from a string.
* A blank line is defined to be a line where the only characters are whitespace.
* We always ensure that the line contains a newline at the end.
* @param text The string to strip blank lines from
* @return The blank line stripped reply
*/
public static String stripBlankLines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
boolean doneOneLine = false;
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
if (line.trim().length() > 0)
{
output.append(line);
output.append('/n');
doneOneLine = true;
}
}
if (!doneOneLine)
{
output.append('/n');
}
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Remove all newline characters from a string.
* @param text The string to strip newline characters from
* @return The stripped reply
*/
public static String stripNewlines(String text)
{
if (text == null)
{
return null;
}
try
{
StringBuffer output = new StringBuffer();
BufferedReader in = new BufferedReader(new StringReader(text));
while (true)
{
String line = in.readLine();
if (line == null)
{
break;
}
output.append(line);
output.append(SPACE);
}
output.append('/n');
return output.toString();
}
catch (IOException ex)
{
log.error("IOExecption unexpected.", ex);
throw new IllegalArgumentException("IOExecption unexpected.");
}
}
/**
* Shrink variable names to a minimum.
* @param text The javascript program to shrink the variable names in.
* @return The shrunk version of the javascript program.
*/
public static String shrinkVariableNames(String text)
{
if (text == null)
{
return null;
}
throw new UnsupportedOperationException("Variable name shrinking is not supported");
}
/**
* <p>Escapes the characters in a <code>String</code> using JavaScript String rules.</p>
* <p>Escapes any values it finds into their JavaScript String form.
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* <p>So a tab becomes the characters <code>'//'</code> and
* <code>'t'</code>.</p>
*
* <p>The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote must be escaped.</p>
*
* <p>Example:
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn/'t say, /"Stop!/"
* </pre>
* </p>
*
* @param str String to escape values in, may be null
* @return String with escaped values, <code>null</code> if null string input
*/
public static String escapeJavaScript(String str)
{
if (str == null)
{
return null;
}
StringBuffer writer = new StringBuffer(str.length() * 2);
int sz = str.length();
for (int i = 0; i < sz; i++)
{
char ch = str.charAt(i);
// handle unicode
if (ch > 0xfff)
{
writer.append("//u");
writer.append(hex(ch));
}
else if (ch > 0xff)
{
writer.append("//u0");
writer.append(hex(ch));
}
else if (ch > 0x7f)
{
writer.append("//u00");
writer.append(hex(ch));
}
else if (ch < 32)
{
switch (ch)
{
case '/b':
writer.append('//');
writer.append('b');
break;
case '/n':
writer.append('//');
writer.append('n');
break;
case '/t':
writer.append('//');
writer.append('t');
break;
case '/f':
writer.append('//');
writer.append('f');
break;
case '/r':
writer.append('//');
writer.append('r');
break;
default:
if (ch > 0xf)
{
writer.append("//u00");
writer.append(hex(ch));
}
else
{
writer.append("//u000");
writer.append(hex(ch));
}
break;
}
}
else
{
switch (ch)
{
case '/'':
// If we wanted to escape for Java strings then we would
// not need this next line.
writer.append('//');
writer.append('/'');
break;
case '"':
writer.append('//');
writer.append('"');
break;
case '//':
writer.append('//');
writer.append('//');
break;
case '<':
writer.append("//u003c");
break;
case '>':
writer.append("//u003e");
break;
default:
writer.append(ch);
break;
}
}
}
return writer.toString();
}
/**
* <p>Returns an upper case hexadecimal <code>String</code> for the given
* character.</p>
* @param ch The character to convert.
* @return An upper case hexadecimal <code>String</code>
*/
private static String hex(char ch)
{
return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
}
/**
* <p>Unescapes any JavaScript literals found in the <code>String</code>.</p>
* <p>For example, it will turn a sequence of <code>'/'</code> and <code>'n'</code>
* into a newline character, unless the <code>'/'</code> is preceded by another
* <code>'/'</code>.</p>
* @param str the <code>String</code> to unescape, may be null
* @return A new unescaped <code>String</code>, <code>null</code> if null string input
*/
public static String unescapeJavaScript(String str)
{
if (str == null)
{
return null;
}
StringBuffer writer = new StringBuffer(str.length());
int sz = str.length();
StringBuffer unicode = new StringBuffer(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0; i < sz; i++)
{
char ch = str.charAt(i);
if (inUnicode)
{
// if in unicode, then we're reading unicode
// values in somehow
unicode.append(ch);
if (unicode.length() == 4)
{
// unicode now contains the four hex digits
// which represents our unicode chacater
try
{
int value = Integer.parseInt(unicode.toString(), 16);
writer.append((char) value);
unicode.setLength(0);
inUnicode = false;
hadSlash = false;
}
catch (NumberFormatException nfe)
{
throw new IllegalArgumentException("Unable to parse unicode value: " + unicode + " cause: " + nfe);
}
}
continue;
}
if (hadSlash)
{
// handle an escaped value
hadSlash = false;
switch (ch)
{
case '//':
writer.append('//');
break;
case '/'':
writer.append('/'');
break;
case '/"':
writer.append('"');
break;
case 'r':
writer.append('/r');
break;
case 'f':
writer.append('/f');
break;
case 't':
writer.append('/t');
break;
case 'n':
writer.append('/n');
break;
case 'b':
writer.append('/b');
break;
case 'u':
// uh-oh, we're in unicode country....
inUnicode = true;
break;
default:
writer.append(ch);
break;
}
continue;
}
else if (ch == '//')
{
hadSlash = true;
continue;
}
writer.append(ch);
}
if (hadSlash)
{
// then we're in the weird case of a / at the end of the
// string, let's output it anyway.
writer.append('//');
}
return writer.toString();
}
/**
* Check to see if the given word is reserved or a bad idea in any known
* version of JavaScript.
* @param name The word to check
* @return false if the word is not reserved
*/
public static boolean isReservedWord(String name)
{
return reserved.contains(name);
}
/**
* The array of javascript reserved words
*/
private static final String[] RESERVED_ARRAY = new String[]
{
// Reserved and used at ECMAScript 4
"as",
"break",
"case",
"catch",
"class",
"const",
"continue",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"is",
"namespace",
"new",
"null",
"package",
"private",
"public",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"use",
"var",
"void",
"while",
"with",
// Reserved for future use at ECMAScript 4
"abstract",
"debugger",
"enum",
"goto",
"implements",
"interface",
"native",
"protected",
"synchronized",
"throws",
"transient",
"volatile",
// Reserved in ECMAScript 3, unreserved at 4 best to avoid anyway
"boolean",
"byte",
"char",
"double",
"final",
"float",
"int",
"long",
"short",
"static",
// I have seen the folowing list as 'best avoided for function names'
// but it seems way to all encompassing, so I'm not going to include it
/*
"alert", "anchor", "area", "arguments", "array", "assign", "blur",
"boolean", "button", "callee", "caller", "captureevents", "checkbox",
"clearinterval", "cleartimeout", "close", "closed", "confirm",
"constructor", "date", "defaultstatus", "document", "element", "escape",
"eval", "fileupload", "find", "focus", "form", "frame", "frames",
"getclass", "hidden", "history", "home", "image", "infinity",
"innerheight", "isfinite", "innerwidth", "isnan", "java", "javaarray",
"javaclass", "javaobject", "javapackage", "length", "link", "location",
"locationbar", "math", "menubar", "mimetype", "moveby", "moveto",
"name", "nan", "navigate", "navigator", "netscape", "number", "object",
"onblur", "onerror", "onfocus", "onload", "onunload", "open", "opener",
"option", "outerheight", "outerwidth", "packages", "pagexoffset",
"pageyoffset", "parent", "parsefloat", "parseint", "password",
"personalbar", "plugin", "print", "prompt", "prototype", "radio", "ref",
"regexp", "releaseevents", "reset", "resizeby", "resizeto",
"routeevent", "scroll", "scrollbars", "scrollby", "scrollto", "select",
"self", "setinterval", "settimeout", "status", "statusbar", "stop",
"string", "submit", "sun", "taint", "text", "textarea", "toolbar",
"top", "tostring", "unescape", "untaint", "unwatch", "valueof", "watch",
"window",
*/
};
private static SortedSet reserved = new TreeSet();
/**
* For easy access ...
*/
static
{
// The Javascript reserved words array so we don't generate illegal javascript
reserved.addAll(Arrays.asList(RESERVED_ARRAY));
}
private static final String SPACE = " ";
/**
* How does a multi line comment start?
*/
private static final String COMMENT_ML_START = "/*";
/**
* How does a multi line comment end?
*/
private static final String COMMENT_ML_END = "*/";
/**
* How does a single line comment start?
*/
private static final String COMMENT_SL_START = "//";
/**
* Sometimes we need to retain the comment because it has special meaning
*/
private static final String COMMENT_RETAIN = "#DWR";
/**
* The log stream
*/
private static final Log log = LogFactory.getLog(JavascriptUtil.class);
}
需要的JSON类我已经放到共享,你可以从
http://download.csdn.net/source/879579
下载
相关文章推荐
- 自己编写的一个Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加载对象
- 自己编写的一个Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加载对象
- Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加
- json数据与JAVA数据的转换 jsonJavaBean.netApache 自己编写了一个工具类,处理页面提交json格式数据到后台,再进行处理成JAVA对象数据 1、DTO:Data T
- 利用Java反射实现普通对象到JSONObject的转换
- writeObject可以写n个,但是readObject()却只能读一次,你做一下测试。 如果你需要序列化好几个类的话,建议你用json;或者自己写两个方法,一个是将对象转换为字符串,一个是将字符串
- 之前写的一个XML工具类,包括将JSON对象转换为XML对象
- 使用java反射实现对象工厂以及工具类的编写
- //1、有一个字符串开头或结尾含有n个空格(” abcdefgdddd ”),欲去掉前后空格,返回一个新字符串。 //要求1:请自己定义一个接口(函数),并实现功能;70分 //要求2:编写
- 对象转换为Map集合,使用JSONObject实现
- 尝试自己写一个工具类实现UGUI的按钮功能
- Jquery将一个object对象转换成json字符串
- 通过java的反射机制实现Map、JavaBean、JSON的相互转换工具类
- jQuery实现form表单序列化转换为json对象功能示例
- json解决hibernate中级联对象延迟加载问题net.sf.json.JSONException: org....
- 自己实现一个SQL解析引擎 功能:将用户输入的SQL语句序列转换为一个可执行的操作序列,并返回查询的结果集。 SQL的解析引擎包括查询编译与查询优化和查询的运行,主要包括3个步骤: 查询分析
- 用自己的思路编写一个$.extend()/Object.assign()对象合并函数
- 一个实用的 Javascript XML to JSON Object 对象的转换 (JQuery)
- Hibernate实现实体对象延迟加载(转载)
- JSONArray,JSONObject工具类实现Json,bean,map,list等的转换