com.xinqihd.sns.gameserver.util.JSON.java Source code

Java tutorial

Introduction

Here is the source code for com.xinqihd.sns.gameserver.util.JSON.java

Source

/**
 *      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();
    }

}