/* | |
* Copyright 1999-2011 Alibaba Group. | |
* | |
* 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.alibaba.dubbo.common.json; | |
import java.io.IOException; | |
import java.io.Reader; | |
import java.io.StringReader; | |
import java.io.StringWriter; | |
import java.io.Writer; | |
import com.alibaba.dubbo.common.bytecode.Wrapper; | |
import com.alibaba.dubbo.common.utils.Stack; | |
/** | |
* JSON. | |
* | |
* @author qian.lei | |
*/ | |
public class JSON | |
{ | |
public static final char LBRACE = '{', RBRACE = '}'; | |
public static final char LSQUARE = '[', RSQUARE = ']'; | |
public static final char COMMA = ',', COLON = ':', QUOTE = '"'; | |
public static final String NULL = "null"; | |
static final JSONConverter DEFAULT_CONVERTER = new GenericJSONConverter(); | |
// state. | |
public static final byte END = 0, START = 1, OBJECT_ITEM = 2, OBJECT_VALUE = 3, ARRAY_ITEM = 4; | |
private static class Entry | |
{ | |
byte state; | |
Object value; | |
Entry(byte s, Object v){ state = s; value = v; } | |
} | |
private JSON(){} | |
/** | |
* json string. | |
* | |
* @param obj object. | |
* @return json string. | |
* @throws IOException. | |
*/ | |
public static String json(Object obj) throws IOException | |
{ | |
if( obj == null ) return NULL; | |
StringWriter sw = new StringWriter(); | |
try | |
{ | |
json(obj, sw); | |
return sw.getBuffer().toString(); | |
} | |
finally{ sw.close(); } | |
} | |
/** | |
* write json. | |
* | |
* @param obj object. | |
* @param writer writer. | |
* @throws IOException. | |
*/ | |
public static void json(Object obj, Writer writer) throws IOException | |
{ | |
json(obj, writer, false); | |
} | |
public static void json(Object obj, Writer writer, boolean writeClass) throws IOException | |
{ | |
if( obj == null ) | |
writer.write(NULL); | |
else | |
json(obj, new JSONWriter(writer), writeClass); | |
} | |
/** | |
* json string. | |
* | |
* @param obj object. | |
* @param properties property name array. | |
* @return json string. | |
* @throws IOException. | |
*/ | |
public static String json(Object obj, String[] properties) throws IOException | |
{ | |
if( obj == null ) return NULL; | |
StringWriter sw = new StringWriter(); | |
try | |
{ | |
json(obj, properties, sw); | |
return sw.getBuffer().toString(); | |
} | |
finally{ sw.close(); } | |
} | |
public static void json(Object obj, final String[] properties, Writer writer) throws IOException | |
{ | |
json(obj, properties, writer, false); | |
} | |
/** | |
* write json. | |
* | |
* @param obj object. | |
* @param properties property name array. | |
* @param writer writer. | |
* @throws IOException. | |
*/ | |
public static void json(Object obj, final String[] properties, Writer writer, boolean writeClass) throws IOException | |
{ | |
if( obj == null ) | |
writer.write(NULL); | |
else | |
json(obj, properties, new JSONWriter(writer), writeClass); | |
} | |
private static void json(Object obj, JSONWriter jb, boolean writeClass) throws IOException | |
{ | |
if( obj == null ) | |
jb.valueNull(); | |
else | |
DEFAULT_CONVERTER.writeValue(obj, jb, writeClass); | |
} | |
private static void json(Object obj, String[] properties, JSONWriter jb, boolean writeClass) throws IOException | |
{ | |
if( obj == null ) | |
{ | |
jb.valueNull(); | |
} | |
else | |
{ | |
Wrapper wrapper = Wrapper.getWrapper(obj.getClass()); | |
Object value; | |
jb.objectBegin(); | |
for( String prop : properties ) | |
{ | |
jb.objectItem(prop); | |
value = wrapper.getPropertyValue(obj, prop); | |
if( value == null ) | |
jb.valueNull(); | |
else | |
DEFAULT_CONVERTER.writeValue(value, jb, writeClass); | |
} | |
jb.objectEnd(); | |
} | |
} | |
/** | |
* parse json. | |
* | |
* @param json json source. | |
* @return JSONObject or JSONArray or Boolean or Long or Double or String or null | |
* @throws ParseException | |
*/ | |
public static Object parse(String json) throws ParseException | |
{ | |
StringReader reader = new StringReader(json); | |
try{ return parse(reader); } | |
catch(IOException e){ throw new ParseException(e.getMessage()); } | |
finally{ reader.close(); } | |
} | |
/** | |
* parse json. | |
* | |
* @param reader reader. | |
* @return JSONObject or JSONArray or Boolean or Long or Double or String or null | |
* @throws IOException | |
* @throws ParseException | |
*/ | |
public static Object parse(Reader reader) throws IOException, ParseException | |
{ | |
return parse(reader, JSONToken.ANY); | |
} | |
/** | |
* parse json. | |
* | |
* @param json json string. | |
* @param type target type. | |
* @return result. | |
* @throws ParseException | |
*/ | |
public static <T> T parse(String json, Class<T> type) throws ParseException | |
{ | |
StringReader reader = new StringReader(json); | |
try{ return parse(reader, type); } | |
catch(IOException e){ throw new ParseException(e.getMessage()); } | |
finally{ reader.close(); } | |
} | |
/** | |
* parse json | |
* | |
* @param reader json source. | |
* @param type target type. | |
* @return result. | |
* @throws IOException | |
* @throws ParseException | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T parse(Reader reader, Class<T> type) throws IOException, ParseException | |
{ | |
return (T)parse(reader, new J2oVisitor(type, DEFAULT_CONVERTER), JSONToken.ANY); | |
} | |
/** | |
* parse json. | |
* | |
* @param json json string. | |
* @param types target type array. | |
* @return result. | |
* @throws ParseException | |
*/ | |
public static Object[] parse(String json, Class<?>[] types) throws ParseException | |
{ | |
StringReader reader = new StringReader(json); | |
try{ return (Object[])parse(reader, types); } | |
catch(IOException e){ throw new ParseException(e.getMessage()); } | |
finally{ reader.close(); } | |
} | |
/** | |
* parse json. | |
* | |
* @param reader json source. | |
* @param types target type array. | |
* @return result. | |
* @throws IOException | |
* @throws ParseException | |
*/ | |
public static Object[] parse(Reader reader, Class<?>[] types) throws IOException, ParseException | |
{ | |
return (Object[])parse(reader, new J2oVisitor(types, DEFAULT_CONVERTER), JSONToken.LSQUARE); | |
} | |
/** | |
* parse json. | |
* | |
* @param json json string. | |
* @param handler handler. | |
* @return result. | |
* @throws ParseException | |
*/ | |
public static Object parse(String json, JSONVisitor handler) throws ParseException | |
{ | |
StringReader reader = new StringReader(json); | |
try{ return parse(reader, handler); } | |
catch(IOException e){ throw new ParseException(e.getMessage()); } | |
finally{ reader.close(); } | |
} | |
/** | |
* parse json. | |
* | |
* @param reader json source. | |
* @param handler handler. | |
* @return resule. | |
* @throws IOException | |
* @throws ParseException | |
*/ | |
public static Object parse(Reader reader, JSONVisitor handler) throws IOException, ParseException | |
{ | |
return parse(reader, handler, JSONToken.ANY); | |
} | |
private static Object parse(Reader reader, int expect) throws IOException, ParseException | |
{ | |
JSONReader jr = new JSONReader(reader); | |
JSONToken token = jr.nextToken(expect); | |
byte state = START; | |
Object value = null, tmp; | |
Stack<Entry> stack = new Stack<Entry>(); | |
do | |
{ | |
switch( state ) | |
{ | |
case END: | |
throw new ParseException("JSON source format error."); | |
case START: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING: | |
{ | |
state = END; | |
value = token.value; | |
break; | |
} | |
case JSONToken.LSQUARE: | |
{ | |
state = ARRAY_ITEM; | |
value = new JSONArray(); | |
break; | |
} | |
case JSONToken.LBRACE: | |
{ | |
state = OBJECT_ITEM; | |
value = new JSONObject(); | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case ARRAY_ITEM: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COMMA: | |
break; | |
case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING: | |
{ | |
((JSONArray)value).add(token.value); | |
break; | |
} | |
case JSONToken.RSQUARE: // end of array. | |
{ | |
if( stack.isEmpty() ) | |
{ | |
state = END; | |
} | |
else | |
{ | |
Entry entry = stack.pop(); | |
state = entry.state; | |
value = entry.value; | |
} | |
break; | |
} | |
case JSONToken.LSQUARE: // array begin. | |
{ | |
tmp = new JSONArray(); | |
((JSONArray)value).add(tmp); | |
stack.push(new Entry(state, value)); | |
state = ARRAY_ITEM; | |
value = tmp; | |
break; | |
} | |
case JSONToken.LBRACE: // object begin. | |
{ | |
tmp = new JSONObject(); | |
((JSONArray)value).add(tmp); | |
stack.push(new Entry(state, value)); | |
state = OBJECT_ITEM; | |
value = tmp; | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case OBJECT_ITEM: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COMMA: | |
break; | |
case JSONToken.IDENT: // item name. | |
{ | |
stack.push(new Entry(OBJECT_ITEM, (String)token.value)); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.NULL: | |
{ | |
stack.push(new Entry(OBJECT_ITEM, "null")); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING: | |
{ | |
stack.push(new Entry(OBJECT_ITEM, token.value.toString())); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.RBRACE: // end of object. | |
{ | |
if( stack.isEmpty() ) | |
{ | |
state = END; | |
} | |
else | |
{ | |
Entry entry = stack.pop(); | |
state = entry.state; | |
value = entry.value; | |
} | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case OBJECT_VALUE: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COLON: | |
break; | |
case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING: | |
{ | |
((JSONObject)value).put((String)stack.pop().value, token.value); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.LSQUARE: // array begin. | |
{ | |
tmp = new JSONArray(); | |
((JSONObject)value).put((String)stack.pop().value, tmp); | |
stack.push(new Entry(OBJECT_ITEM, value)); | |
state = ARRAY_ITEM; | |
value = tmp; | |
break; | |
} | |
case JSONToken.LBRACE: // object begin. | |
{ | |
tmp = new JSONObject(); | |
((JSONObject)value).put((String)stack.pop().value, tmp); | |
stack.push(new Entry(OBJECT_ITEM, value)); | |
state = OBJECT_ITEM; | |
value = tmp; | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted state."); | |
} | |
} | |
while( ( token = jr.nextToken() ) != null ); | |
stack.clear(); | |
return value; | |
} | |
private static Object parse(Reader reader, JSONVisitor handler, int expect) throws IOException, ParseException | |
{ | |
JSONReader jr = new JSONReader(reader); | |
JSONToken token = jr.nextToken(expect); | |
Object value = null; | |
int state = START, index = 0; | |
Stack<int[]> states = new Stack<int[]>(); | |
boolean pv = false; | |
handler.begin(); | |
do | |
{ | |
switch( state ) | |
{ | |
case END: | |
throw new ParseException("JSON source format error."); | |
case START: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.NULL: | |
{ | |
value = token.value; | |
state = END; | |
pv = true; | |
break; | |
} | |
case JSONToken.BOOL: | |
{ | |
value = token.value; | |
state = END; | |
pv = true; | |
break; | |
} | |
case JSONToken.INT: | |
{ | |
value = token.value; | |
state = END; | |
pv = true; | |
break; | |
} | |
case JSONToken.FLOAT: | |
{ | |
value = token.value; | |
state = END; | |
pv = true; | |
break; | |
} | |
case JSONToken.STRING: | |
{ | |
value = token.value; | |
state = END; | |
pv = true; | |
break; | |
} | |
case JSONToken.LSQUARE: | |
{ | |
handler.arrayBegin(); | |
state = ARRAY_ITEM; | |
break; | |
} | |
case JSONToken.LBRACE: | |
{ | |
handler.objectBegin(); | |
state = OBJECT_ITEM; | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case ARRAY_ITEM: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COMMA: | |
break; | |
case JSONToken.NULL: | |
{ | |
handler.arrayItem(index++); | |
handler.arrayItemValue(index, token.value, true); | |
break; | |
} | |
case JSONToken.BOOL: | |
{ | |
handler.arrayItem(index++); | |
handler.arrayItemValue(index, token.value, true); | |
break; | |
} | |
case JSONToken.INT: | |
{ | |
handler.arrayItem(index++); | |
handler.arrayItemValue(index, token.value, true); | |
break; | |
} | |
case JSONToken.FLOAT: | |
{ | |
handler.arrayItem(index++); | |
handler.arrayItemValue(index, token.value, true); | |
break; | |
} | |
case JSONToken.STRING: | |
{ | |
handler.arrayItem(index++); | |
handler.arrayItemValue(index, token.value, true); | |
break; | |
} | |
case JSONToken.LSQUARE: | |
{ | |
handler.arrayItem(index++); | |
states.push(new int[]{state, index}); | |
index = 0; | |
state = ARRAY_ITEM; | |
handler.arrayBegin(); | |
break; | |
} | |
case JSONToken.LBRACE: | |
{ | |
handler.arrayItem(index++); | |
states.push(new int[]{state, index}); | |
index = 0; | |
state = OBJECT_ITEM; | |
handler.objectBegin(); | |
break; | |
} | |
case JSONToken.RSQUARE: | |
{ | |
if( states.isEmpty() ) | |
{ | |
value = handler.arrayEnd(index); | |
state = END; | |
} | |
else | |
{ | |
value = handler.arrayEnd(index); | |
int[] tmp = states.pop(); | |
state = tmp[0]; | |
index = tmp[1]; | |
switch( state ) | |
{ | |
case ARRAY_ITEM: | |
{ | |
handler.arrayItemValue(index, value, false); | |
break; | |
} | |
case OBJECT_ITEM: | |
{ | |
handler.objectItemValue(value, false); | |
break; | |
} | |
} | |
} | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case OBJECT_ITEM: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COMMA: | |
break; | |
case JSONToken.IDENT: | |
{ | |
handler.objectItem((String)token.value); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.NULL: | |
{ | |
handler.objectItem("null"); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING: | |
{ | |
handler.objectItem(token.value.toString()); | |
state = OBJECT_VALUE; | |
break; | |
} | |
case JSONToken.RBRACE: | |
{ | |
if( states.isEmpty() ) | |
{ | |
value = handler.objectEnd(index); | |
state = END; | |
} | |
else | |
{ | |
value = handler.objectEnd(index); | |
int[] tmp = states.pop(); | |
state = tmp[0]; | |
index = tmp[1]; | |
switch( state ) | |
{ | |
case ARRAY_ITEM: | |
{ | |
handler.arrayItemValue(index, value, false); | |
break; | |
} | |
case OBJECT_ITEM: | |
{ | |
handler.objectItemValue(value, false); | |
break; | |
} | |
} | |
} | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
case OBJECT_VALUE: | |
{ | |
switch( token.type ) | |
{ | |
case JSONToken.COLON: | |
break; | |
case JSONToken.NULL: | |
{ | |
handler.objectItemValue(token.value, true); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.BOOL: | |
{ | |
handler.objectItemValue(token.value, true); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.INT: | |
{ | |
handler.objectItemValue(token.value, true); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.FLOAT: | |
{ | |
handler.objectItemValue(token.value, true); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.STRING: | |
{ | |
handler.objectItemValue(token.value, true); | |
state = OBJECT_ITEM; | |
break; | |
} | |
case JSONToken.LSQUARE: | |
{ | |
states.push(new int[]{OBJECT_ITEM, index}); | |
index = 0; | |
state = ARRAY_ITEM; | |
handler.arrayBegin(); | |
break; | |
} | |
case JSONToken.LBRACE: | |
{ | |
states.push(new int[]{OBJECT_ITEM, index}); | |
index = 0; | |
state = OBJECT_ITEM; | |
handler.objectBegin(); | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); | |
} | |
break; | |
} | |
default: | |
throw new ParseException("Unexcepted state."); | |
} | |
} | |
while( ( token = jr.nextToken() ) != null ); | |
states.clear(); | |
return handler.end(value, pv); | |
} | |
} |