/* | |
* 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.OutputStream; | |
import java.io.OutputStreamWriter; | |
import java.io.UnsupportedEncodingException; | |
import java.io.Writer; | |
import com.alibaba.dubbo.common.utils.Stack; | |
/** | |
* JSON Writer. | |
* | |
* w.objectBegin().objectItem("name").valueString("qianlei").objectEnd() = {name:"qianlei"}. | |
* | |
* @author qian.lei | |
*/ | |
public class JSONWriter | |
{ | |
private static final byte UNKNOWN = 0, ARRAY = 1, OBJECT = 2, OBJECT_VALUE = 3; | |
private static class State | |
{ | |
private byte type; | |
private int itemCount = 0; | |
State(byte t){ type = t; } | |
} | |
private Writer mWriter; | |
private State mState = new State(UNKNOWN); | |
private Stack<State> mStack = new Stack<State>(); | |
public JSONWriter(Writer writer) | |
{ | |
mWriter = writer; | |
} | |
public JSONWriter(OutputStream is, String charset) throws UnsupportedEncodingException | |
{ | |
mWriter = new OutputStreamWriter(is, charset); | |
} | |
/** | |
* object begin. | |
* | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter objectBegin() throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(JSON.LBRACE); | |
mStack.push(mState); | |
mState = new State(OBJECT); | |
return this; | |
} | |
/** | |
* object end. | |
* | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter objectEnd() throws IOException | |
{ | |
mWriter.write(JSON.RBRACE); | |
mState = mStack.pop(); | |
return this; | |
} | |
/** | |
* object item. | |
* | |
* @param name name. | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter objectItem(String name) throws IOException | |
{ | |
beforeObjectItem(); | |
mWriter.write(JSON.QUOTE); | |
mWriter.write(escape(name)); | |
mWriter.write(JSON.QUOTE); | |
mWriter.write(JSON.COLON); | |
return this; | |
} | |
/** | |
* array begin. | |
* | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter arrayBegin() throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(JSON.LSQUARE); | |
mStack.push(mState); | |
mState = new State(ARRAY); | |
return this; | |
} | |
/** | |
* array end, return array value. | |
* | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter arrayEnd() throws IOException | |
{ | |
mWriter.write(JSON.RSQUARE); | |
mState = mStack.pop(); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @return this. | |
* @throws IOException. | |
*/ | |
public JSONWriter valueNull() throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(JSON.NULL); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueString(String value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(JSON.QUOTE); | |
mWriter.write(escape(value)); | |
mWriter.write(JSON.QUOTE); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueBoolean(boolean value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(value ? "true" : "false"); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueInt(int value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(String.valueOf(value)); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueLong(long value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(String.valueOf(value)); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueFloat(float value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(String.valueOf(value)); | |
return this; | |
} | |
/** | |
* value. | |
* | |
* @param value value. | |
* @return this. | |
* @throws IOException | |
*/ | |
public JSONWriter valueDouble(double value) throws IOException | |
{ | |
beforeValue(); | |
mWriter.write(String.valueOf(value)); | |
return this; | |
} | |
private void beforeValue() throws IOException | |
{ | |
switch( mState.type ) | |
{ | |
case ARRAY: | |
if( mState.itemCount++ > 0 ) | |
mWriter.write(JSON.COMMA); | |
return; | |
case OBJECT: | |
throw new IOException("Must call objectItem first."); | |
case OBJECT_VALUE: | |
mState.type = OBJECT; | |
return; | |
} | |
} | |
private void beforeObjectItem() throws IOException | |
{ | |
switch( mState.type ) | |
{ | |
case OBJECT_VALUE: | |
mWriter.write(JSON.NULL); | |
case OBJECT: | |
mState.type = OBJECT_VALUE; | |
if( mState.itemCount++ > 0 ) | |
mWriter.write(JSON.COMMA); | |
return; | |
default: | |
throw new IOException("Must call objectBegin first."); | |
} | |
} | |
private static final String[] CONTROL_CHAR_MAP = new String[]{ | |
"\\u0000","\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007", | |
"\\b","\\t","\\n","\\u000b","\\f","\\r","\\u000e","\\u000f", | |
"\\u0010","\\u0011","\\u0012","\\u0013","\\u0014","\\u0015","\\u0016","\\u0017", | |
"\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d","\\u001e","\\u001f" | |
}; | |
private static String escape(String str) | |
{ | |
if( str == null ) | |
return str; | |
int len = str.length(); | |
if( len == 0 ) | |
return str; | |
char c; | |
StringBuilder sb = null; | |
for(int i=0;i<len;i++) | |
{ | |
c = str.charAt(i); | |
if( c < ' ' ) // control char. | |
{ | |
if( sb == null ) | |
{ | |
sb = new StringBuilder(len<<1); | |
sb.append(str,0,i); | |
} | |
sb.append(CONTROL_CHAR_MAP[c]); | |
} | |
else | |
{ | |
switch( c ) | |
{ | |
case '\\': case '/': case '"': | |
if( sb == null ) | |
{ | |
sb = new StringBuilder(len<<1); | |
sb.append(str,0,i); | |
} | |
sb.append('\\').append(c); | |
break; | |
default: | |
if( sb != null ) | |
sb.append(c); | |
} | |
} | |
} | |
return sb == null ? str : sb.toString(); | |
} | |
} |