package org.nutz.json.impl; import java.io.IOException; import java.io.Reader; import java.math.BigDecimal; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.nutz.json.JsonException; import org.nutz.json.JsonParser; import org.nutz.lang.Lang; import org.nutz.mapl.MaplCompile; /** * 将json理解为Map+List,以Token的方式读取,避免回溯等操作 * * @author wendal(wendal1985@gmail.com) * */ public class JsonCompileImplV2 implements JsonParser, MaplCompile<Reader> { public Object parse(Reader reader) { return new JsonTokenScan(reader).read(); } } final class JsonTokenScan { // private static final Log log = Logs.get(); 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() { // System.out.println("_nextToken > " + (char) token.type); 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; // log.debug("Break OtherString token : " + (char) c); // log.debug("OtherString token : " + (char) token.type); 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(); // log.debug("OtherString Token > " + token.value); return; } } protected void nextToken() { if (nextToken != null) { token.type = nextToken.type; token.value = nextToken.value; nextToken = null; return; } token.type = nextChar(); _nextToken(); // log.debug("token: " + token); } protected void skipComment() { char c = nextChar(); switch (c) { case '/': // 单行注释 while (nextChar() != '\n') {} // nextToken(); 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; // log.debug("key=" + key + " " + token); 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(); // System.out.println(">>>> " + token.type + " " + token); 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 JsonException(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 + Lang.readAll(reader); else nextToken.value = Lang.readAll(reader); //System.out.println("VVVVV>>>>>>>" + nextToken.value); return readObject(-1); } } char nextChar() { int c = readChar(); // System.out.println("+++++++++++===>> " + (char) c); if (c == -1) throw new JsonException("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 JsonException(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 JsonException unexpectChar(char c) { return new JsonException(row, col, c, "Unexpect Char"); } } class JsonToken { int type; String value; public String toString() { return "[" + (char) type + " " + value + "]" + hashCode(); } }