com.netease.stream.util.json.JSONWriter Maven / Gradle / Ivy
package com.netease.stream.util.json;
import java.io.IOException;
import java.io.Writer;
import java.util.Date;
/**
* JSONWriter provides a quick and convenient way of producing JSON text. The texts produced
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
* transmission or storage. Each instance of JSONWriter can produce one JSON text.
*
* A JSONWriter instance provides a value
method for appending values to the text, and
* a key
method for adding keys before values in objects. There are array
* and endArray
methods that make and bound array values, and object
and
* endObject
methods which make and bound object values. All of these methods return
* the JSONWriter instance, permitting a cascade style. For example,
*
*
* new JSONWriter(myWriter).object().key("JSON").value("Hello, World!").endObject();
*
*
* which writes
*
*
* {"JSON":"Hello, World!"}
*
*
* The first method called must be array
or object
. There are no methods
* for adding commas or colons. JSONWriter adds them for you. Objects and arrays can be nested up to
* 20 levels deep.
*
* This can sometimes be easier than using a JSONObject to build a string.
*
* @version 2010-03-11
*/
public class JSONWriter {
private static final int maxdepth = 20;
/**
* The comma flag determines if a comma should be output before the next value.
*/
private boolean comma;
/**
* The current mode. Values: 'a' (array), 'd' (done), 'i' (initial), 'k' (key), 'o' (object).
*/
protected char mode;
/**
* The object/array stack.
*/
private JSONObject stack[];
/**
* The stack top index. A value of 0 indicates that the stack is empty.
*/
private int top;
/**
* The writer that will receive the output.
*/
protected Writer writer;
/**
* Make a fresh JSONWriter. It can be used to build one JSON text.
*
* @param w the Writer.
*/
public JSONWriter(Writer w) {
this.comma = false;
this.mode = 'i';
this.stack = new JSONObject[maxdepth];
this.top = 0;
this.writer = w;
}
/**
* Append a value.
*
* @param s A string value.
* @return this.
* @throws JSONException If the value is out of sequence.
*/
private JSONWriter append(String s) throws JSONException {
if (s == null) {
throw new JSONException("Null pointer");
}
if (this.mode == 'o' || this.mode == 'a') {
try {
if (this.comma && this.mode == 'a') {
this.writer.write(',');
}
this.writer.write(s);
} catch (IOException e) {
throw new JSONException(e);
}
if (this.mode == 'o') {
this.mode = 'k';
}
this.comma = true;
return this;
}
throw new JSONException("Value out of sequence.");
}
/**
* Begin appending a new array. All values until the balancing endArray
will be
* appended to this array. The endArray
method must be called to mark the array's
* end.
*
* @return this
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
* place (for example as a key or after the end of the outermost array or object).
*/
public JSONWriter array() throws JSONException {
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
this.push(null);
this.append("[");
this.comma = false;
return this;
}
throw new JSONException("Misplaced array.");
}
/**
* End something.
*
* @param m Mode.
* @param c Closing character.
* @return this.
* @throws JSONException If unbalanced.
*/
private JSONWriter end(char m, char c) throws JSONException {
if (this.mode != m) {
throw new JSONException(m == 'a' ? "Misplaced endArray." : "Misplaced endObject.");
}
this.pop(m);
try {
this.writer.write(c);
} catch (IOException e) {
throw new JSONException(e);
}
this.comma = true;
return this;
}
/**
* End an array. This method most be called to balance calls to array
.
*
* @return this.
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endArray() throws JSONException {
return this.end('a', ']');
}
/**
* End an object. This method most be called to balance calls to object
.
*
* @return this.
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endObject() throws JSONException {
return this.end('k', '}');
}
/**
* Append a key. The key will be associated with the next value. In an object, every value must
* be preceded by a key.
*
* @param s A key string.
* @return this.
* @throws JSONException If the key is out of place. For example, keys do not belong in arrays
* or if the key is null.
*/
public JSONWriter key(String s) throws JSONException {
if (s == null) {
throw new JSONException("Null key.");
}
if (this.mode == 'k') {
try {
stack[top - 1].putOnce(s, Boolean.TRUE);
if (this.comma) {
this.writer.write(',');
}
this.writer.write(JSONObject.quote(s));
this.writer.write(':');
this.comma = false;
this.mode = 'o';
return this;
} catch (IOException e) {
throw new JSONException(e);
}
}
throw new JSONException("Misplaced key.");
}
/**
* Begin appending a new object. All keys and values until the balancing endObject
* will be appended to this object. The endObject
method must be called to mark the
* object's end.
*
* @return this.
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
* place (for example as a key or after the end of the outermost array or object).
*/
public JSONWriter object() throws JSONException {
if (this.mode == 'i') {
this.mode = 'o';
}
if (this.mode == 'o' || this.mode == 'a') {
this.append("{");
this.push(new JSONObject());
this.comma = false;
return this;
}
throw new JSONException("Misplaced object.");
}
/**
* Pop an array or object scope.
*
* @param c The scope to close.
* @throws JSONException If nesting is wrong.
*/
private void pop(char c) throws JSONException {
if (this.top <= 0) {
throw new JSONException("Nesting error.");
}
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
if (m != c) {
throw new JSONException("Nesting error.");
}
this.top -= 1;
this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
}
/**
* Push an array or object scope.
*
* @param c The scope to open.
* @throws JSONException If nesting is too deep.
*/
private void push(JSONObject jo) throws JSONException {
if (this.top >= maxdepth) {
throw new JSONException("Nesting too deep.");
}
this.stack[this.top] = jo;
this.mode = jo == null ? 'a' : 'k';
this.top += 1;
}
/**
* Append either the value true
or the value false
.
*
* @param b A boolean.
* @return this.
* @throws JSONException JSONException.
*/
public JSONWriter value(boolean b) throws JSONException {
return this.append(b ? "true" : "false");
}
/**
* Append a double value.
*
* @param d A double.
* @return this.
* @throws JSONException If the number is not finite.
*/
public JSONWriter value(double d) throws JSONException {
return this.value(new Double(d));
}
/**
* Append a long value.
*
* @param l A long.
* @return this.
* @throws JSONException JSONException.
*/
public JSONWriter value(long l) throws JSONException {
return this.value(new Long(l));
}
/**
* Appends a date value, as epoch seconds.
*
* @param date The date to append.
* @return this.
* @throws JSONException JSONException.
*/
public JSONWriter value(Date date) throws JSONException {
return this.value(new Long(date.getTime() / 1000));
}
/**
* Append an object value.
*
* @param o The object to append. It can be null, or a Boolean, Number, String, JSONObject, or
* JSONArray, or an object with a toJSONString() method.
* @return this
* @throws JSONException If the value is out of sequence.
*/
public JSONWriter value(Object o) throws JSONException {
return this.append(JSONObject.valueToString(o));
}
}