package li.json; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; /** * 将json理解为Map+List,以Token的方式读取,避免回溯等操作 * * @author wendal(wendal1985@gmail.com) */ public class Json { public static Object fromJson(String string) { return new JsonCompileImplV2().parse(new StringReader(string)); } public static String toJson(Object obj) { Writer writer = new StringWriter(); try { new JsonRenderImpl(writer, JsonFormat.nice()).render(obj); writer.flush(); } catch (IOException e) { throw new RuntimeException(e); } return writer.toString(); } } /** * 将json理解为Map+List,以Token的方式读取,避免回溯等操作 * * @author wendal(wendal1985@gmail.com) */ class JsonCompileImplV2 { public Object parse(Reader reader) { return new JsonTokenScan(reader).read(); } } final class JsonTokenScan { Reader reader; JsonToken token = new JsonToken(); JsonToken nextToken = null; JsonToken nextToken2 = new JsonToken(); static final Object END = new Object(); static final Object COMMA = new Object(); public JsonTokenScan(Reader reader) { this.reader = reader; } protected void _nextToken() { switch (token.type) { case MapStart: case MapEnd: case ListStart: case ListEnd: case MapPair: case Comma: return; case '\'': token.type = SimpleString; token.value = readString('\''); return; case '\"': token.type = SimpleString; token.value = readString('"'); return; case ' ': case '\t': case '\n': case '\r': char c = 0; while (true) { c = nextChar(); switch (c) { case ' ': case '\t': case '\n': case '\r': continue; } break; } token.type = c; _nextToken(); return; case '/': // 看来是注释哦 skipComment(); nextToken(); return; default: StringBuilder sb = new StringBuilder(); sb.append((char) token.type); // 看来只是尝试找到结束字符了 OUT: while (true) { c = nextChar(); switch (c) { case MapStart: case MapEnd: case ListStart: case ListEnd: case MapPair: case Comma: nextToken = nextToken2; nextToken.type = c; break OUT; case ' ': case '\t': case '\r': case '\n': break OUT; case '/': skipComment(); break OUT; } sb.append(c); } token.type = OtherString; token.value = sb.toString(); return; } } protected void nextToken() { if (nextToken != null) { token.type = nextToken.type; token.value = nextToken.value; nextToken = null; return; } token.type = nextChar(); _nextToken(); } protected void skipComment() { char c = nextChar(); switch (c) { case '/': // 单行注释 while (nextChar() != '\n') {} return; case '*': char c2 = c; while (true) { while ((c = nextChar()) != '/') { c2 = c; } if (c2 == '*') return; } default: throw unexpectChar(c); } } protected String readString(char endEnd) { StringBuilder sb = new StringBuilder(); char c = 0; while ((c = nextChar()) != endEnd) { switch (c) { case '\\': c = parseSp(); break; } sb.append(c); } return sb.toString(); } protected Map<String, Object> readMap() { Map<String, Object> map = new LinkedHashMap<String, Object>(); boolean hasComma = false; OUT: while (true) { nextToken(); switch (token.type) { case MapEnd: break OUT; case SimpleString: case OtherString: String key = token.value; nextToken(); if (token.type != MapPair) { throw unexpectChar((char) token.type); } Object obj = readObject(MapEnd); if (obj == COMMA) { if (hasComma) throw unexpectChar((char) Comma); hasComma = true; continue; } if (obj == END) throw unexpectChar((char) token.type); map.put(key, obj); hasComma = false; break; case Comma: continue; default: throw unexpectChar((char) token.type); } } return map; } protected List<Object> readList() { List<Object> list = new ArrayList<Object>(); boolean hasComma = false; while (true) { Object obj = readObject(ListEnd); if (obj == END) break; if (obj == COMMA) { if (hasComma) throw unexpectChar((char) Comma); hasComma = true; continue; } list.add(obj); hasComma = false; } return list; } protected Object readObject(int endTag) { nextToken(); switch (token.type) { case MapStart: return readMap(); case ListStart: return readList(); case SimpleString: return token.value; case OtherString: String value = token.value; int len = value.length(); if (len == 0) return ""; switch (value.charAt(0)) { case 't': if ("true".equals(value)) return true; break; case 'f': if ("false".equals(value)) return false; break; case 'n': if ("null".endsWith(value)) return null; break; case 'u': if ("undefined".endsWith(value)) return null; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '-': // 看来是数字哦 if (token.value.length() > 0) { switch (token.value.charAt(token.value.length() - 1)) { case 'l': case 'L': return Long.parseLong(token.value.substring(0, token.value.length() - 1)); case 'f': case 'F': return Float.parseFloat(token.value.substring(0, token.value.length() - 1)); default: if (token.value.contains("e") || token.value.contains("E")) { return new BigDecimal(token.value); } if (token.value.contains(".")) { return Double.parseDouble(token.value); } } } long n = Long.parseLong(token.value); if (Integer.MAX_VALUE >= n && n >= Integer.MIN_VALUE) { return (int) n; } return n; } throw new RuntimeException(row + col + value.charAt(0) + "Unexpect String = " + value); default: if (token.type == endTag) return END; if (token.type == Comma) return COMMA; throw unexpectChar((char) token.type); } } public Object read() { int c = 0; boolean add = false; OUT: while (true) { c = readChar(); switch (c) { case -1: return null; case ' ': case '\t': case '\n': case '\r': continue; case '/': skipComment(); break; default: add = true; break OUT; } } switch (c) { case 'v': while (nextChar() != MapStart) {} return readMap(); case MapStart: return readMap(); case ListStart: return readList(); case '\'': case '"': return readString((char) c); default: nextToken = nextToken2; nextToken.type = OtherString; if (add) nextToken.value = (char) c + readAll(reader); else nextToken.value = readAll(reader); return readObject(-1); } } char nextChar() { int c = readChar(); if (c == -1) throw new RuntimeException("Unexpect EOF"); return (char) c; } protected char parseSp() { char c = nextChar(); switch (c) { case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case '\\': return '\\'; case '\'': return '\''; case '\"': return '"'; case '/': return '/'; case 'u': char[] hex = new char[4]; for (int i = 0; i < 4; i++) hex[i] = nextChar(); return (char) Integer.valueOf(new String(hex), 16).intValue(); case 'b': // 这个支持一下又何妨? return ' ';// 空格 case 'f': return '\f'; default: throw unexpectChar(c); } } int row = 1; int col = 0; private int readChar() { try { int c = reader.read(); switch (c) { case -1: break; case '\n': row++; col = 0; default: col++; break; } return c; } catch (IOException e) { throw new RuntimeException(e); } } static final int MapStart = '{'; static final int MapEnd = '}'; static final int ListStart = '['; static final int ListEnd = ']'; static final int MapPair = ':'; static final int SimpleString = 0; static final int OtherString = 1; static final int Comma = ','; protected RuntimeException unexpectChar(char c) { return new RuntimeException(row + col + c + "Unexpect Char"); } /** * 从一个文本输入流读取所有内容,并将该流关闭 * * @param reader 文本输入流 * @return 输入流所有内容 */ public static String readAll(Reader reader) { if (!(reader instanceof BufferedReader)) reader = new BufferedReader(reader); try { StringBuilder sb = new StringBuilder(); char[] data = new char[64]; int len; while (true) { if ((len = reader.read(data)) == -1) break; sb.append(data, 0, len); } return sb.toString(); } catch (IOException e) { new RuntimeException(e); } finally { try { reader.close(); } catch (Exception e) { new RuntimeException(e); } } return null; } } class JsonToken { int type; String value; public String toString() { return "[" + (char) type + " " + value + "]" + hashCode(); } } class JsonRenderImpl { private static String NL = "\n"; private JsonFormat format; private Writer writer; private Set<Object> memo = new HashSet<Object>(); public void render(Object obj) throws IOException { if (null == obj) { writer.write("null"); } else if (obj instanceof JsonRenderImpl) { ((JsonRenderImpl) obj).render(null); } else if (obj instanceof Class) { string2Json(((Class<?>) obj).getName()); } else { // 枚举 if (obj.getClass().isEnum()) { string2Json(((Enum) obj).name()); } // 数字,布尔等 else if (isNumber(obj.getClass()) || isBoolean(obj.getClass())) { writer.append(obj.toString()); } // 字符串 else if (CharSequence.class.isAssignableFrom(obj.getClass()) || (is(obj.getClass(), char.class) || is(obj.getClass(), Character.class))) { string2Json(obj.toString()); } // 日期时间 else if (isDateTimeLike(obj.getClass())) { string2Json(obj.toString()); } // 其他 else { // Map if (obj instanceof Map) { map2Json((Map) obj); } // 集合 else if (obj instanceof Collection) { coll2Json((Collection) obj); } // 数组 else if (obj.getClass().isArray()) { array2Json(obj); } // 普通 Java 对象 else { memo.add(obj); pojo2Json(obj); memo.remove(obj); } } } } private boolean is(Class<? extends Object> class1, Class<Character> class2) { // TODO Auto-generated method stub return false; } private boolean isBoolean(Class<? extends Object> class1) { // TODO Auto-generated method stub return false; } private boolean isNumber(Class<? extends Object> class1) { // TODO Auto-generated method stub return false; } private boolean isDateTimeLike(Class<? extends Object> class1) { // TODO Auto-generated method stub return false; } public JsonRenderImpl(Writer writer, JsonFormat format) { this.format = format; this.writer = writer; } private static boolean isCompact(JsonRenderImpl render) { return render.format.isCompact(); } private static final Pattern p = Pattern.compile("^[a-z_A-Z$]+[a-zA-Z_0-9$]*$"); private void appendName(String name) throws IOException { if (format.isQuoteName() || !p.matcher(name).find()) string2Json(name); else writer.append(name); } private void appendPairBegin() throws IOException { if (!isCompact(this)) writer.append(NL).append(dup(format.getIndentBy(), format.getIndent())); } private CharSequence dup(String indentBy, int indent) { // TODO Auto-generated method stub return null; } private void appendPairSep() throws IOException { writer.append(!isCompact(this) ? " :" : ":"); } protected void appendPair(boolean needPairEnd, String name, Object value) throws IOException { appendPairBegin(); appendName(name); appendPairSep(); render(value); if (needPairEnd) { appendPairEnd(); } } private boolean isIgnore(String name, Object value) { if (null == value && format.isIgnoreNull()) return true; return format.ignore(name); } private void appendPairEnd() throws IOException { writer.append(','); } private void appendBraceBegin() throws IOException { writer.append('{'); } private void appendBraceEnd() throws IOException { if (!isCompact(this)) writer.append(NL).append(dup(format.getIndentBy(), format.getIndent())); writer.append('}'); } static class Pair { public Pair(String name, Object value) { this.name = name; this.value = value; } String name; Object value; } private void map2Json(Map map) throws IOException { if (null == map) return; appendBraceBegin(); increaseFormatIndent(); ArrayList<Pair> list = new ArrayList<Pair>(map.size()); Set<Entry<?, ?>> entrySet = map.entrySet(); for (Entry entry : entrySet) { String name = null == entry.getKey() ? "null" : entry.getKey().toString(); Object value = entry.getValue(); if (!this.isIgnore(name, value)) list.add(new Pair(name, value)); } writeItem(list); } private void pojo2Json(Object obj) throws IOException { if (null == obj) return; Class<?> type = obj.getClass(); List<Field> fields = getFields(type); appendBraceBegin(); increaseFormatIndent(); ArrayList<Pair> list = new ArrayList<Pair>(fields.size()); for (Field jef : fields) { String name = jef.getName(); try { Object value = get(obj, jef); // 判断是否应该被忽略 if (!this.isIgnore(name, value)) { // 以前曾经输出过 ... if (null != value) { // zozoh: 循环引用的默认行为,应该为 null,以便和其他语言交换数据 if (memo.contains(value)) value = null; } // 加入输出列表 ... list.add(new Pair(name, value)); } } catch (Exception e) {} } writeItem(list); } private Object get(Object obj, Field jef) { // TODO Auto-generated method stub return null; } private List<Field> getFields(Class<?> type) { // TODO Auto-generated method stub return null; } private void writeItem(List<Pair> list) throws IOException { Iterator<Pair> it = list.iterator(); while (it.hasNext()) { Pair p = it.next(); appendPair(it.hasNext(), p.name, p.value); } decreaseFormatIndent(); appendBraceEnd(); } private void decreaseFormatIndent() { if (!isCompact(this)) format.decreaseIndent(); } private void increaseFormatIndent() { if (!isCompact(this)) format.increaseIndent(); } private void string2Json(String s) throws IOException { if (null == s) writer.append("null"); else { char[] cs = s.toCharArray(); writer.append(format.getSeparator()); for (char c : cs) { switch (c) { case '"': writer.append("\\\""); break; case '\n': writer.append("\\n"); break; case '\t': writer.append("\\t"); break; case '\r': writer.append("\\r"); break; case '\\': writer.append("\\\\"); break; default: if (c >= 256 && format.isAutoUnicode()) writer.append("\\u").append(Integer.toHexString(c).toUpperCase()); else writer.append(c); } } writer.append(format.getSeparator()); } } private void array2Json(Object obj) throws IOException { writer.append('['); int len = Array.getLength(obj) - 1; if (len > -1) { int i; for (i = 0; i < len; i++) { render(Array.get(obj, i)); appendPairEnd(); writer.append(' '); } render(Array.get(obj, i)); } writer.append(']'); } private void coll2Json(Collection iterable) throws IOException { writer.append('['); for (Iterator<?> it = iterable.iterator(); it.hasNext();) { render(it.next()); if (it.hasNext()) { appendPairEnd(); writer.append(' '); } else break; } writer.append(']'); } } /** * 描述Json输出的格式 * * @author zozoh(zozohtnt@gmail.com) * @author Wendal(wendal1985@gmail.com) */ class JsonFormat { /** * 紧凑模式 -- 无换行,忽略null值 */ public static JsonFormat compact() { return new JsonFormat(true).setIgnoreNull(true); } /** * 全部输出模式 -- 换行,不忽略null值 */ public static JsonFormat full() { return new JsonFormat(false).setIgnoreNull(false); } /** * 一般模式 -- 换行,但忽略null值 */ public static JsonFormat nice() { return new JsonFormat(false).setIgnoreNull(true); } /** * 为了打印出来容易看,把名字去掉引号 */ public static JsonFormat forLook() { return new JsonFormat(false).setQuoteName(false).setIgnoreNull(true); } public JsonFormat() { this(true); } public JsonFormat(boolean compact) { this.compact = compact; this.indentBy = " "; this.quoteName = true; this.separator = '\"'; } /** * 缩进 */ private int indent; /** * 缩进时用的字符串 */ private String indentBy; /** * 紧凑 */ private boolean compact; private boolean quoteName; /** * 是否忽略null值 */ private boolean ignoreNull; private Pattern actived; private Pattern locked; /** * 分隔符 */ private char separator; /** * 是否自动将值应用Unicode编码 */ private boolean autoUnicode; public boolean ignore(String name) { if (null != actived) return !actived.matcher(name).find(); if (null != locked) return locked.matcher(name).find(); return false; } public boolean isCompact() { return compact; } public JsonFormat setCompact(boolean compact) { this.compact = compact; return this; } public int getIndent() { return indent; } public JsonFormat setIndent(int indent) { this.indent = indent; return this; } public JsonFormat increaseIndent() { this.indent++; return this; } public JsonFormat decreaseIndent() { this.indent--; return this; } public String getIndentBy() { return indentBy; } public JsonFormat setIndentBy(String indentBy) { this.indentBy = indentBy; return this; } public boolean isQuoteName() { return quoteName; } public JsonFormat setQuoteName(boolean qn) { this.quoteName = qn; return this; } public boolean isIgnoreNull() { return ignoreNull; } public JsonFormat setIgnoreNull(boolean ignoreNull) { this.ignoreNull = ignoreNull; return this; } public JsonFormat setActived(String regex) { this.actived = Pattern.compile(regex); return this; } public JsonFormat setLocked(String regex) { this.locked = Pattern.compile(regex); return this; } public JsonFormat setSeparator(char separator) { this.separator = separator; return this; } public char getSeparator() { return separator; } public JsonFormat setAutoUnicode(boolean autoUnicode) { this.autoUnicode = autoUnicode; return this; } public boolean isAutoUnicode() { return autoUnicode; } }