Java tutorial
/** * Copyright (C) 2008 10gen Inc. * * 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.xinqihd.sns.gameserver.util; import java.lang.reflect.Array; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import java.util.Map; import java.util.Set; import java.util.SimpleTimeZone; import java.util.UUID; import java.util.regex.Pattern; import org.bson.BSONCallback; import org.bson.types.BSONTimestamp; import org.bson.types.Binary; import org.bson.types.Code; import org.bson.types.CodeWScope; import org.bson.types.MaxKey; import org.bson.types.MinKey; import org.bson.types.ObjectId; import com.mongodb.BasicDBObject; import com.mongodb.Bytes; import com.mongodb.DBObject; import com.mongodb.DBRefBase; import com.mongodb.util.JSONCallback; import com.mongodb.util.JSONParseException; /** * Helper methods for JSON serialization and de-serialization */ public class JSON { /** * Serializes an object into it's JSON form * * @param o * object to serialize * @return String containing JSON form of the object */ public static String serialize(Object o) { StringBuilder buf = new StringBuilder(); serialize(o, buf); return buf.toString(); } static void string(StringBuilder a, String s) { a.append("\""); for (int i = 0; i < s.length(); ++i) { char c = s.charAt(i); if (c == '\\') a.append("\\\\"); else if (c == '"') a.append("\\\""); else if (c == '\n') a.append("\\n"); else if (c == '\r') a.append("\\r"); else if (c == '\t') a.append("\\t"); else if (c == '\b') a.append("\\b"); else if (c < 32) continue; else a.append(c); } a.append("\""); } @SuppressWarnings("unchecked") public static void serialize(Object o, StringBuilder buf) { o = Bytes.applyEncodingHooks(o); if (o == null) { buf.append(" null "); return; } if (o instanceof Number) { buf.append(o); return; } if (o instanceof String) { string(buf, o.toString()); return; } if (o instanceof Iterable) { boolean first = true; buf.append("[ "); for (Object n : (Iterable) o) { if (first) first = false; else buf.append(" , "); serialize(n, buf); } buf.append("]"); return; } if (o instanceof ObjectId) { serialize(new BasicDBObject("$oid", o.toString()), buf); return; } if (o instanceof DBObject) { boolean first = true; buf.append("{ "); DBObject dbo = (DBObject) o; for (String name : dbo.keySet()) { if (first) first = false; else buf.append(" , "); string(buf, name); buf.append(" : "); serialize(dbo.get(name), buf); } buf.append("}"); return; } if (o instanceof Map) { boolean first = true; buf.append("{ "); Map m = (Map) o; for (Map.Entry entry : (Set<Map.Entry>) m.entrySet()) { if (first) first = false; else buf.append(" , "); string(buf, entry.getKey().toString()); buf.append(" : "); serialize(entry.getValue(), buf); } buf.append("}"); return; } if (o instanceof Date) { Date d = (Date) o; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); serialize(new BasicDBObject("$date", format.format(d)), buf); return; } if (o instanceof DBRefBase) { DBRefBase ref = (DBRefBase) o; BasicDBObject temp = new BasicDBObject(); temp.put("$ref", ref.getRef()); temp.put("$id", ref.getId()); serialize(temp, buf); return; } if (o instanceof Boolean) { buf.append(o); return; } if (o instanceof byte[] || o instanceof Binary) { byte[] array = null; if (o instanceof byte[]) { array = (byte[]) o; } else { array = ((Binary) o).getData(); } buf.append("\"").append(StringUtil.bytesToHexString(array)).append("\""); return; } if (o instanceof Pattern) { DBObject externalForm = new BasicDBObject(); externalForm.put("$regex", o.toString()); externalForm.put("$options", Bytes.regexFlags(((Pattern) o).flags())); serialize(externalForm, buf); return; } if (o.getClass().isArray()) { buf.append("[ "); for (int i = 0; i < Array.getLength(o); i++) { if (i > 0) buf.append(" , "); serialize(Array.get(o, i), buf); } buf.append("]"); return; } if (o instanceof BSONTimestamp) { BSONTimestamp t = (BSONTimestamp) o; BasicDBObject temp = new BasicDBObject(); temp.put("$ts", t.getTime()); temp.put("$inc", t.getInc()); serialize(temp, buf); return; } if (o instanceof UUID) { UUID uuid = (UUID) o; BasicDBObject temp = new BasicDBObject(); temp.put("$uuid", uuid.toString()); serialize(temp, buf); return; } if (o instanceof CodeWScope) { CodeWScope c = (CodeWScope) o; BasicDBObject temp = new BasicDBObject(); temp.put("$code", c.getCode()); temp.put("$scope", c.getScope()); serialize(temp, buf); return; } if (o instanceof Code) { Code c = (Code) o; BasicDBObject temp = new BasicDBObject(); temp.put("$code", c.getCode()); serialize(temp, buf); return; } if (o instanceof MinKey) { serialize(new BasicDBObject("$minKey", 1), buf); return; } if (o instanceof MaxKey) { serialize(new BasicDBObject("$maxKey", 1), buf); return; } throw new RuntimeException("json can't serialize type : " + o.getClass()); } /** * Parses a JSON string representing a JSON value * * @param s * the string to parse * @return the object */ public static Object parse(String s) { return parse(s, null); } /** * Parses a JSON string representing a JSON value * * @param s * the string to parse * @return the object */ public static Object parse(String s, BSONCallback c) { if (s == null || (s = s.trim()).equals("")) { return (DBObject) null; } JSONParser p = new JSONParser(s, c); return p.parse(); } } /** * Parser for JSON objects. * * Supports all types described at www.json.org, except for numbers with "e" or * "E" in them. */ class JSONParser { String s; int pos = 0; BSONCallback _callback; /** * Create a new parser. */ public JSONParser(String s) { this(s, null); } /** * Create a new parser. */ public JSONParser(String s, BSONCallback callback) { this.s = s; _callback = (callback == null) ? new JSONCallback() : callback; } /** * Parse an unknown type. * * @return Object the next item * @throws JSONParseException * if invalid JSON is found */ public Object parse() { return parse(null); } /** * Parse an unknown type. * * @return Object the next item * @throws JSONParseException * if invalid JSON is found */ protected Object parse(String name) { Object value = null; char current = get(); switch (current) { // null case 'n': read('n'); read('u'); read('l'); read('l'); value = null; break; // NaN case 'N': read('N'); read('a'); read('N'); value = Double.NaN; break; // true case 't': read('t'); read('r'); read('u'); read('e'); value = true; break; // false case 'f': read('f'); read('a'); read('l'); read('s'); read('e'); value = false; break; // string case '\'': case '\"': value = parseString(true); break; // number case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '+': case '-': value = parseNumber(); break; // array case '[': value = parseArray(name); break; // object case '{': value = parseObject(name); break; default: throw new JSONParseException(s, pos); } return value; } /** * Parses an object for the form <i>{}</i> and <i>{ members }</i>. * * @return DBObject the next object * @throws JSONParseException * if invalid JSON is found */ public Object parseObject() { return parseObject(null); } /** * Parses an object for the form <i>{}</i> and <i>{ members }</i>. * * @return DBObject the next object * @throws JSONParseException * if invalid JSON is found */ protected Object parseObject(String name) { if (name != null) { _callback.objectStart(name); } else { _callback.objectStart(); } read('{'); char current = get(); while (get() != '}') { String key = parseString(false); read(':'); Object value = parse(key); doCallback(key, value); if ((current = get()) == ',') { read(','); } else { break; } } read('}'); return _callback.objectDone(); } protected void doCallback(String name, Object value) { if (value == null) { _callback.gotNull(name); } else if (value instanceof String) { _callback.gotString(name, (String) value); } else if (value instanceof Boolean) { _callback.gotBoolean(name, (Boolean) value); } else if (value instanceof Integer) { _callback.gotInt(name, (Integer) value); } else if (value instanceof Long) { _callback.gotLong(name, (Long) value); } else if (value instanceof Double) { _callback.gotDouble(name, (Double) value); } } /** * Read the current character, making sure that it is the expected character. * Advances the pointer to the next character. * * @param ch * the character expected * * @throws JSONParseException * if the current character does not match the given character */ public void read(char ch) { if (!check(ch)) { throw new JSONParseException(s, pos); } pos++; } public char read() { if (pos >= s.length()) throw new IllegalStateException("string done"); return s.charAt(pos++); } /** * Read the current character, making sure that it is a hexidecimal character. * * @throws JSONParseException * if the current character is not a hexidecimal character */ public void readHex() { if (pos < s.length() && ((s.charAt(pos) >= '0' && s.charAt(pos) <= '9') || (s.charAt(pos) >= 'A' && s.charAt(pos) <= 'F') || (s.charAt(pos) >= 'a' && s.charAt(pos) <= 'f'))) { pos++; } else { throw new JSONParseException(s, pos); } } /** * Checks the current character, making sure that it is the expected * character. * * @param ch * the character expected * * @throws JSONParseException * if the current character does not match the given character */ public boolean check(char ch) { return get() == ch; } /** * Advances the position in the string past any whitespace. */ public void skipWS() { while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) { pos++; } } /** * Returns the current character. Returns -1 if there are no more characters. * * @return the next character */ public char get() { skipWS(); if (pos < s.length()) return s.charAt(pos); return (char) -1; } /** * Parses a string. * * @return the next string. * @throws JSONParseException * if invalid JSON is found */ public String parseString(boolean needQuote) { char quot = 0; if (check('\'')) quot = '\''; else if (check('\"')) quot = '\"'; else if (needQuote) throw new JSONParseException(s, pos); char current; if (quot > 0) read(quot); StringBuilder buf = new StringBuilder(); int start = pos; while (pos < s.length()) { current = s.charAt(pos); if (quot > 0) { if (current == quot) break; } else { if (current == ':' || current == ' ') break; } if (current == '\\') { pos++; char x = get(); char special = 0; switch (x) { case 'u': { // decode unicode buf.append(s.substring(start, pos - 1)); pos++; int tempPos = pos; readHex(); readHex(); readHex(); readHex(); int codePoint = Integer.parseInt(s.substring(tempPos, tempPos + 4), 16); buf.append((char) codePoint); start = pos; continue; } case 'n': special = '\n'; break; case 'r': special = '\r'; break; case 't': special = '\t'; break; case 'b': special = '\b'; break; case '"': special = '\"'; break; case '\\': special = '\\'; break; } buf.append(s.substring(start, pos - 1)); if (special != 0) { pos++; buf.append(special); } start = pos; continue; } pos++; } buf.append(s.substring(start, pos)); if (quot > 0) read(quot); return buf.toString(); } /** * Parses a number. * * @return the next number (int or double). * @throws JSONParseException * if invalid JSON is found */ public Number parseNumber() { char current = get(); int start = this.pos; boolean isDouble = false; if (check('-') || check('+')) { pos++; } outer: while (pos < s.length()) { switch (s.charAt(pos)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pos++; break; case '.': isDouble = true; parseFraction(); break; case 'e': case 'E': isDouble = true; parseExponent(); break; default: break outer; } } if (isDouble) return Double.valueOf(s.substring(start, pos)); Long val = Long.valueOf(s.substring(start, pos)); if (val <= Integer.MAX_VALUE) return val.intValue(); return val; } /** * Advances the pointed through <i>.digits</i>. */ public void parseFraction() { // get past . pos++; outer: while (pos < s.length()) { switch (s.charAt(pos)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pos++; break; case 'e': case 'E': parseExponent(); break; default: break outer; } } } /** * Advances the pointer through the exponent. */ public void parseExponent() { // get past E pos++; if (check('-') || check('+')) { pos++; } outer: while (pos < s.length()) { switch (s.charAt(pos)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pos++; break; default: break outer; } } } /** * Parses the next array. * * @return the array * @throws JSONParseException * if invalid JSON is found */ public Object parseArray() { return parseArray(null); } /** * Parses the next array. * * @return the array * @throws JSONParseException * if invalid JSON is found */ protected Object parseArray(String name) { if (name != null) { _callback.arrayStart(name); } else { _callback.arrayStart(); } read('['); int i = 0; char current = get(); while (current != ']') { String elemName = String.valueOf(i++); Object elem = parse(elemName); doCallback(elemName, elem); if ((current = get()) == ',') { read(','); } else if (current == ']') { break; } else { throw new JSONParseException(s, pos); } } read(']'); return _callback.arrayDone(); } }