mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Make JsonReader and JsonWriter our own.
Drop proprietary Gson features (non-execute prefix, HTML safe chars). Don't serialize nulls by default. Add a String constructor to JsonReader. Begin to migrate JsonReader to using Okio's buffer.
This commit is contained in:
@@ -18,7 +18,9 @@ package com.squareup.moshi;
|
|||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import okio.Buffer;
|
||||||
|
import okio.BufferedSource;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
|
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
|
||||||
@@ -166,26 +168,12 @@ import java.io.Reader;
|
|||||||
* precision loss, extremely large values should be written and read as strings
|
* precision loss, extremely large values should be written and read as strings
|
||||||
* in JSON.
|
* in JSON.
|
||||||
*
|
*
|
||||||
* <a name="nonexecuteprefix"/><h3>Non-Execute Prefix</h3>
|
|
||||||
* Web servers that serve private data using JSON may be vulnerable to <a
|
|
||||||
* href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
|
|
||||||
* request forgery</a> attacks. In such an attack, a malicious site gains access
|
|
||||||
* to a private JSON file by executing it with an HTML {@code <script>} tag.
|
|
||||||
*
|
|
||||||
* <p>Prefixing JSON files with <code>")]}'\n"</code> makes them non-executable
|
|
||||||
* by {@code <script>} tags, disarming the attack. Since the prefix is malformed
|
|
||||||
* JSON, strict parsing fails when it is encountered. This class permits the
|
|
||||||
* non-execute prefix when {@link #setLenient(boolean) lenient parsing} is
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
|
* <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
|
||||||
* of this class are not thread safe.
|
* of this class are not thread safe.
|
||||||
*
|
*
|
||||||
* @author Jesse Wilson
|
* @author Jesse Wilson
|
||||||
*/
|
*/
|
||||||
public class JsonReader implements Closeable {
|
public class JsonReader implements Closeable {
|
||||||
/** The only non-execute prefix this parser permits */
|
|
||||||
private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray();
|
|
||||||
private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
|
private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
|
||||||
|
|
||||||
private static final int PEEKED_NONE = 0;
|
private static final int PEEKED_NONE = 0;
|
||||||
@@ -219,24 +207,13 @@ public class JsonReader implements Closeable {
|
|||||||
private static final int NUMBER_CHAR_EXP_SIGN = 6;
|
private static final int NUMBER_CHAR_EXP_SIGN = 6;
|
||||||
private static final int NUMBER_CHAR_EXP_DIGIT = 7;
|
private static final int NUMBER_CHAR_EXP_DIGIT = 7;
|
||||||
|
|
||||||
/** The input JSON. */
|
|
||||||
private final Reader in;
|
|
||||||
|
|
||||||
/** True to accept non-spec compliant JSON */
|
/** True to accept non-spec compliant JSON */
|
||||||
private boolean lenient = false;
|
private boolean lenient = false;
|
||||||
|
|
||||||
/**
|
/** The input JSON. */
|
||||||
* Use a manual buffer to easily read and unread upcoming characters, and
|
private final BufferedSource source;
|
||||||
* also so we can create strings without an intermediate StringBuilder.
|
private final Buffer buffer;
|
||||||
* We decode literals directly out of this buffer, so it must be at least as
|
|
||||||
* long as the longest token that can be reported as a number.
|
|
||||||
*/
|
|
||||||
private final char[] buffer = new char[1024];
|
|
||||||
private int pos = 0;
|
|
||||||
private int limit = 0;
|
|
||||||
|
|
||||||
private int lineNumber = 0;
|
|
||||||
private int lineStart = 0;
|
|
||||||
|
|
||||||
private int peeked = PEEKED_NONE;
|
private int peeked = PEEKED_NONE;
|
||||||
|
|
||||||
@@ -282,11 +259,19 @@ public class JsonReader implements Closeable {
|
|||||||
/**
|
/**
|
||||||
* Creates a new instance that reads a JSON-encoded stream from {@code in}.
|
* Creates a new instance that reads a JSON-encoded stream from {@code in}.
|
||||||
*/
|
*/
|
||||||
public JsonReader(Reader in) {
|
public JsonReader(Source source) {
|
||||||
if (in == null) {
|
if (source == null) {
|
||||||
throw new NullPointerException("in == null");
|
throw new NullPointerException("source == null");
|
||||||
}
|
}
|
||||||
this.in = in;
|
throw new UnsupportedOperationException("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance that reads a JSON-encoded value {@code s}.
|
||||||
|
*/
|
||||||
|
public JsonReader(String s) {
|
||||||
|
this.source = new Buffer().writeUtf8(s);
|
||||||
|
this.buffer = new Buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -344,7 +329,7 @@ public class JsonReader implements Closeable {
|
|||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek()
|
throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +347,7 @@ public class JsonReader implements Closeable {
|
|||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected END_ARRAY but was " + peek()
|
throw new IllegalStateException("Expected END_ARRAY but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +365,7 @@ public class JsonReader implements Closeable {
|
|||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek()
|
throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,7 +384,7 @@ public class JsonReader implements Closeable {
|
|||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected END_OBJECT but was " + peek()
|
throw new IllegalStateException("Expected END_OBJECT but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,6 +448,7 @@ public class JsonReader implements Closeable {
|
|||||||
} else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
|
} else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
|
||||||
// Look for a comma before the next element.
|
// Look for a comma before the next element.
|
||||||
int c = nextNonWhitespace(true);
|
int c = nextNonWhitespace(true);
|
||||||
|
buffer.readByte(); // consume ']' or ','.
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case ']':
|
case ']':
|
||||||
return peeked = PEEKED_END_ARRAY;
|
return peeked = PEEKED_END_ARRAY;
|
||||||
@@ -478,6 +464,7 @@ public class JsonReader implements Closeable {
|
|||||||
// Look for a comma before the next element.
|
// Look for a comma before the next element.
|
||||||
if (peekStack == JsonScope.NONEMPTY_OBJECT) {
|
if (peekStack == JsonScope.NONEMPTY_OBJECT) {
|
||||||
int c = nextNonWhitespace(true);
|
int c = nextNonWhitespace(true);
|
||||||
|
buffer.readByte(); // Consume '}' or ','.
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '}':
|
case '}':
|
||||||
return peeked = PEEKED_END_OBJECT;
|
return peeked = PEEKED_END_OBJECT;
|
||||||
@@ -492,19 +479,21 @@ public class JsonReader implements Closeable {
|
|||||||
int c = nextNonWhitespace(true);
|
int c = nextNonWhitespace(true);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"':
|
case '"':
|
||||||
|
buffer.readByte(); // consume the '\"'.
|
||||||
return peeked = PEEKED_DOUBLE_QUOTED_NAME;
|
return peeked = PEEKED_DOUBLE_QUOTED_NAME;
|
||||||
case '\'':
|
case '\'':
|
||||||
|
buffer.readByte(); // consume the '\''.
|
||||||
checkLenient();
|
checkLenient();
|
||||||
return peeked = PEEKED_SINGLE_QUOTED_NAME;
|
return peeked = PEEKED_SINGLE_QUOTED_NAME;
|
||||||
case '}':
|
case '}':
|
||||||
if (peekStack != JsonScope.NONEMPTY_OBJECT) {
|
if (peekStack != JsonScope.NONEMPTY_OBJECT) {
|
||||||
|
buffer.readByte(); // consume the '}'.
|
||||||
return peeked = PEEKED_END_OBJECT;
|
return peeked = PEEKED_END_OBJECT;
|
||||||
} else {
|
} else {
|
||||||
throw syntaxError("Expected name");
|
throw syntaxError("Expected name");
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
checkLenient();
|
checkLenient();
|
||||||
pos--; // Don't consume the first character in an unquoted string.
|
|
||||||
if (isLiteral((char) c)) {
|
if (isLiteral((char) c)) {
|
||||||
return peeked = PEEKED_UNQUOTED_NAME;
|
return peeked = PEEKED_UNQUOTED_NAME;
|
||||||
} else {
|
} else {
|
||||||
@@ -515,22 +504,20 @@ public class JsonReader implements Closeable {
|
|||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
|
stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
|
||||||
// Look for a colon before the value.
|
// Look for a colon before the value.
|
||||||
int c = nextNonWhitespace(true);
|
int c = nextNonWhitespace(true);
|
||||||
|
buffer.readByte(); // Consume ':'.
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case ':':
|
case ':':
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
checkLenient();
|
checkLenient();
|
||||||
if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
|
if (fillBuffer(1) && buffer.getByte(0) == '>') {
|
||||||
pos++;
|
buffer.readByte(); // Consume '>'.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw syntaxError("Expected ':'");
|
throw syntaxError("Expected ':'");
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
||||||
if (lenient) {
|
|
||||||
consumeNonExecutePrefix();
|
|
||||||
}
|
|
||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
|
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
|
||||||
} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
|
} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
|
||||||
int c = nextNonWhitespace(false);
|
int c = nextNonWhitespace(false);
|
||||||
@@ -538,7 +525,6 @@ public class JsonReader implements Closeable {
|
|||||||
return peeked = PEEKED_EOF;
|
return peeked = PEEKED_EOF;
|
||||||
} else {
|
} else {
|
||||||
checkLenient();
|
checkLenient();
|
||||||
pos--;
|
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.CLOSED) {
|
} else if (peekStack == JsonScope.CLOSED) {
|
||||||
throw new IllegalStateException("JsonReader is closed");
|
throw new IllegalStateException("JsonReader is closed");
|
||||||
@@ -548,6 +534,7 @@ public class JsonReader implements Closeable {
|
|||||||
switch (c) {
|
switch (c) {
|
||||||
case ']':
|
case ']':
|
||||||
if (peekStack == JsonScope.EMPTY_ARRAY) {
|
if (peekStack == JsonScope.EMPTY_ARRAY) {
|
||||||
|
buffer.readByte(); // Consume ']'.
|
||||||
return peeked = PEEKED_END_ARRAY;
|
return peeked = PEEKED_END_ARRAY;
|
||||||
}
|
}
|
||||||
// fall-through to handle ",]"
|
// fall-through to handle ",]"
|
||||||
@@ -556,25 +543,27 @@ public class JsonReader implements Closeable {
|
|||||||
// In lenient mode, a 0-length literal in an array means 'null'.
|
// In lenient mode, a 0-length literal in an array means 'null'.
|
||||||
if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
|
if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
|
||||||
checkLenient();
|
checkLenient();
|
||||||
pos--;
|
|
||||||
return peeked = PEEKED_NULL;
|
return peeked = PEEKED_NULL;
|
||||||
} else {
|
} else {
|
||||||
throw syntaxError("Unexpected value");
|
throw syntaxError("Unexpected value");
|
||||||
}
|
}
|
||||||
case '\'':
|
case '\'':
|
||||||
checkLenient();
|
checkLenient();
|
||||||
|
buffer.readByte(); // Consume '\''.
|
||||||
return peeked = PEEKED_SINGLE_QUOTED;
|
return peeked = PEEKED_SINGLE_QUOTED;
|
||||||
case '"':
|
case '"':
|
||||||
if (stackSize == 1) {
|
if (stackSize == 1) {
|
||||||
checkLenient();
|
checkLenient();
|
||||||
}
|
}
|
||||||
|
buffer.readByte(); // Consume '\"'.
|
||||||
return peeked = PEEKED_DOUBLE_QUOTED;
|
return peeked = PEEKED_DOUBLE_QUOTED;
|
||||||
case '[':
|
case '[':
|
||||||
|
buffer.readByte(); // Consume '['.
|
||||||
return peeked = PEEKED_BEGIN_ARRAY;
|
return peeked = PEEKED_BEGIN_ARRAY;
|
||||||
case '{':
|
case '{':
|
||||||
|
buffer.readByte(); // Consume ']'.
|
||||||
return peeked = PEEKED_BEGIN_OBJECT;
|
return peeked = PEEKED_BEGIN_OBJECT;
|
||||||
default:
|
default:
|
||||||
pos--; // Don't consume the first character in a literal value.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stackSize == 1) {
|
if (stackSize == 1) {
|
||||||
@@ -591,7 +580,7 @@ public class JsonReader implements Closeable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isLiteral(buffer[pos])) {
|
if (!isLiteral(buffer.getByte(0))) {
|
||||||
throw syntaxError("Expected value");
|
throw syntaxError("Expected value");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +590,7 @@ public class JsonReader implements Closeable {
|
|||||||
|
|
||||||
private int peekKeyword() throws IOException {
|
private int peekKeyword() throws IOException {
|
||||||
// Figure out which keyword we're matching against by its first character.
|
// Figure out which keyword we're matching against by its first character.
|
||||||
char c = buffer[pos];
|
byte c = buffer.getByte(0);
|
||||||
String keyword;
|
String keyword;
|
||||||
String keywordUpper;
|
String keywordUpper;
|
||||||
int peeking;
|
int peeking;
|
||||||
@@ -624,31 +613,25 @@ public class JsonReader implements Closeable {
|
|||||||
// Confirm that chars [1..length) match the keyword.
|
// Confirm that chars [1..length) match the keyword.
|
||||||
int length = keyword.length();
|
int length = keyword.length();
|
||||||
for (int i = 1; i < length; i++) {
|
for (int i = 1; i < length; i++) {
|
||||||
if (pos + i >= limit && !fillBuffer(i + 1)) {
|
if (!fillBuffer(i + 1)) {
|
||||||
return PEEKED_NONE;
|
return PEEKED_NONE;
|
||||||
}
|
}
|
||||||
c = buffer[pos + i];
|
c = buffer.getByte(i);
|
||||||
if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
|
if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
|
||||||
return PEEKED_NONE;
|
return PEEKED_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pos + length < limit || fillBuffer(length + 1))
|
if (fillBuffer(length + 1) && isLiteral(buffer.getByte(length))) {
|
||||||
&& isLiteral(buffer[pos + length])) {
|
|
||||||
return PEEKED_NONE; // Don't match trues, falsey or nullsoft!
|
return PEEKED_NONE; // Don't match trues, falsey or nullsoft!
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've found the keyword followed either by EOF or by a non-literal character.
|
// We've found the keyword followed either by EOF or by a non-literal character.
|
||||||
pos += length;
|
buffer.skip(length);
|
||||||
return peeked = peeking;
|
return peeked = peeking;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int peekNumber() throws IOException {
|
private int peekNumber() throws IOException {
|
||||||
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
|
|
||||||
char[] buffer = this.buffer;
|
|
||||||
int p = pos;
|
|
||||||
int l = limit;
|
|
||||||
|
|
||||||
long value = 0; // Negative to accommodate Long.MIN_VALUE more easily.
|
long value = 0; // Negative to accommodate Long.MIN_VALUE more easily.
|
||||||
boolean negative = false;
|
boolean negative = false;
|
||||||
boolean fitsInLong = true;
|
boolean fitsInLong = true;
|
||||||
@@ -658,20 +641,11 @@ public class JsonReader implements Closeable {
|
|||||||
|
|
||||||
charactersOfNumber:
|
charactersOfNumber:
|
||||||
for (; true; i++) {
|
for (; true; i++) {
|
||||||
if (p + i == l) {
|
|
||||||
if (i == buffer.length) {
|
|
||||||
// Though this looks like a well-formed number, it's too long to continue reading. Give up
|
|
||||||
// and let the application handle this as an unquoted literal.
|
|
||||||
return PEEKED_NONE;
|
|
||||||
}
|
|
||||||
if (!fillBuffer(i + 1)) {
|
if (!fillBuffer(i + 1)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p = pos;
|
|
||||||
l = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
char c = buffer[p + i];
|
byte c = buffer.getByte(i);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '-':
|
case '-':
|
||||||
if (last == NUMBER_CHAR_NONE) {
|
if (last == NUMBER_CHAR_NONE) {
|
||||||
@@ -735,7 +709,7 @@ public class JsonReader implements Closeable {
|
|||||||
// We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
|
// We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
|
||||||
if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)) {
|
if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)) {
|
||||||
peekedLong = negative ? value : -value;
|
peekedLong = negative ? value : -value;
|
||||||
pos += i;
|
buffer.skip(i);
|
||||||
return peeked = PEEKED_LONG;
|
return peeked = PEEKED_LONG;
|
||||||
} else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
|
} else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
|
||||||
|| last == NUMBER_CHAR_EXP_DIGIT) {
|
|| last == NUMBER_CHAR_EXP_DIGIT) {
|
||||||
@@ -746,7 +720,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLiteral(char c) throws IOException {
|
private boolean isLiteral(int c) throws IOException {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '/':
|
case '/':
|
||||||
case '\\':
|
case '\\':
|
||||||
@@ -792,7 +766,7 @@ public class JsonReader implements Closeable {
|
|||||||
result = nextQuotedValue('"');
|
result = nextQuotedValue('"');
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected a name but was " + peek()
|
throw new IllegalStateException("Expected a name but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
pathNames[stackSize - 1] = result;
|
pathNames[stackSize - 1] = result;
|
||||||
@@ -825,11 +799,10 @@ public class JsonReader implements Closeable {
|
|||||||
} else if (p == PEEKED_LONG) {
|
} else if (p == PEEKED_LONG) {
|
||||||
result = Long.toString(peekedLong);
|
result = Long.toString(peekedLong);
|
||||||
} else if (p == PEEKED_NUMBER) {
|
} else if (p == PEEKED_NUMBER) {
|
||||||
result = new String(buffer, pos, peekedNumberLength);
|
result = buffer.readUtf8(peekedNumberLength);
|
||||||
pos += peekedNumberLength;
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected a string but was " + peek()
|
throw new IllegalStateException("Expected a string but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
pathIndices[stackSize - 1]++;
|
pathIndices[stackSize - 1]++;
|
||||||
@@ -858,7 +831,7 @@ public class JsonReader implements Closeable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("Expected a boolean but was " + peek()
|
throw new IllegalStateException("Expected a boolean but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -878,7 +851,7 @@ public class JsonReader implements Closeable {
|
|||||||
pathIndices[stackSize - 1]++;
|
pathIndices[stackSize - 1]++;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected null but was " + peek()
|
throw new IllegalStateException("Expected null but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,22 +877,21 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p == PEEKED_NUMBER) {
|
if (p == PEEKED_NUMBER) {
|
||||||
peekedString = new String(buffer, pos, peekedNumberLength);
|
peekedString = buffer.readUtf8(peekedNumberLength);
|
||||||
pos += peekedNumberLength;
|
|
||||||
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
||||||
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
||||||
} else if (p == PEEKED_UNQUOTED) {
|
} else if (p == PEEKED_UNQUOTED) {
|
||||||
peekedString = nextUnquotedValue();
|
peekedString = nextUnquotedValue();
|
||||||
} else if (p != PEEKED_BUFFERED) {
|
} else if (p != PEEKED_BUFFERED) {
|
||||||
throw new IllegalStateException("Expected a double but was " + peek()
|
throw new IllegalStateException("Expected a double but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
peeked = PEEKED_BUFFERED;
|
peeked = PEEKED_BUFFERED;
|
||||||
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
|
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
|
||||||
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
|
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
|
||||||
throw new IOException("JSON forbids NaN and infinities: " + result
|
throw new IOException("JSON forbids NaN and infinities: " + result
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peekedString = null;
|
peekedString = null;
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
@@ -950,8 +922,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p == PEEKED_NUMBER) {
|
if (p == PEEKED_NUMBER) {
|
||||||
peekedString = new String(buffer, pos, peekedNumberLength);
|
peekedString = buffer.readUtf8(peekedNumberLength);
|
||||||
pos += peekedNumberLength;
|
|
||||||
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
||||||
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
||||||
try {
|
try {
|
||||||
@@ -964,7 +935,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected a long but was " + peek()
|
throw new IllegalStateException("Expected a long but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
peeked = PEEKED_BUFFERED;
|
peeked = PEEKED_BUFFERED;
|
||||||
@@ -972,7 +943,7 @@ public class JsonReader implements Closeable {
|
|||||||
long result = (long) asDouble;
|
long result = (long) asDouble;
|
||||||
if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
|
if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
|
||||||
throw new NumberFormatException("Expected a long but was " + peekedString
|
throw new NumberFormatException("Expected a long but was " + peekedString
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peekedString = null;
|
peekedString = null;
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
@@ -991,54 +962,36 @@ public class JsonReader implements Closeable {
|
|||||||
* malformed.
|
* malformed.
|
||||||
*/
|
*/
|
||||||
private String nextQuotedValue(char quote) throws IOException {
|
private String nextQuotedValue(char quote) throws IOException {
|
||||||
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
|
|
||||||
char[] buffer = this.buffer;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
while (true) {
|
int p = 0;
|
||||||
int p = pos;
|
while (fillBuffer(p + 1)) {
|
||||||
int l = limit;
|
int c = buffer.getByte(p++);
|
||||||
/* the index of the first character not yet appended to the builder. */
|
|
||||||
int start = p;
|
|
||||||
while (p < l) {
|
|
||||||
int c = buffer[p++];
|
|
||||||
|
|
||||||
if (c == quote) {
|
if (c == quote) {
|
||||||
pos = p;
|
builder.append(buffer.readUtf8(p - 1));
|
||||||
builder.append(buffer, start, p - start - 1);
|
buffer.readByte();
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
pos = p;
|
builder.append(buffer.readUtf8(p - 1));
|
||||||
builder.append(buffer, start, p - start - 1);
|
buffer.readByte(); // '\'
|
||||||
builder.append(readEscapeCharacter());
|
builder.append(readEscapeCharacter());
|
||||||
p = pos;
|
p = 0;
|
||||||
l = limit;
|
|
||||||
start = p;
|
|
||||||
} else if (c == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
lineStart = p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(buffer, start, p - start);
|
|
||||||
pos = p;
|
|
||||||
if (!fillBuffer(1)) {
|
|
||||||
throw syntaxError("Unterminated string");
|
throw syntaxError("Unterminated string");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unquoted value as a string.
|
* Returns an unquoted value as a string.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("fallthrough")
|
@SuppressWarnings("fallthrough")
|
||||||
private String nextUnquotedValue() throws IOException {
|
private String nextUnquotedValue() throws IOException {
|
||||||
StringBuilder builder = null;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
findNonLiteralCharacter:
|
findNonStringChar:
|
||||||
while (true) {
|
for (; fillBuffer(i + 1); i++) {
|
||||||
for (; pos + i < limit; i++) {
|
switch (buffer.getByte(i)) {
|
||||||
switch (buffer[pos + i]) {
|
|
||||||
case '/':
|
case '/':
|
||||||
case '\\':
|
case '\\':
|
||||||
case ';':
|
case ';':
|
||||||
@@ -1056,74 +1009,35 @@ public class JsonReader implements Closeable {
|
|||||||
case '\f':
|
case '\f':
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
break findNonLiteralCharacter;
|
break findNonStringChar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to load the entire literal into the buffer at once.
|
return buffer.readUtf8(i);
|
||||||
if (i < buffer.length) {
|
|
||||||
if (fillBuffer(i + 1)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use a StringBuilder when the value is too long. This is too long to be a number!
|
|
||||||
if (builder == null) {
|
|
||||||
builder = new StringBuilder();
|
|
||||||
}
|
|
||||||
builder.append(buffer, pos, i);
|
|
||||||
pos += i;
|
|
||||||
i = 0;
|
|
||||||
if (!fillBuffer(1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String result;
|
|
||||||
if (builder == null) {
|
|
||||||
result = new String(buffer, pos, i);
|
|
||||||
} else {
|
|
||||||
builder.append(buffer, pos, i);
|
|
||||||
result = builder.toString();
|
|
||||||
}
|
|
||||||
pos += i;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void skipQuotedValue(char quote) throws IOException {
|
private void skipQuotedValue(char quote) throws IOException {
|
||||||
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
|
int p = 0;
|
||||||
char[] buffer = this.buffer;
|
while (fillBuffer(p + 1)) {
|
||||||
do {
|
int c = buffer.getByte(p++);
|
||||||
int p = pos;
|
|
||||||
int l = limit;
|
|
||||||
/* the index of the first character not yet appended to the builder. */
|
|
||||||
while (p < l) {
|
|
||||||
int c = buffer[p++];
|
|
||||||
if (c == quote) {
|
if (c == quote) {
|
||||||
pos = p;
|
buffer.skip(p);
|
||||||
return;
|
return;
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
pos = p;
|
buffer.skip(p);
|
||||||
readEscapeCharacter();
|
readEscapeCharacter();
|
||||||
p = pos;
|
p = 0;
|
||||||
l = limit;
|
|
||||||
} else if (c == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
lineStart = p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos = p;
|
|
||||||
} while (fillBuffer(1));
|
|
||||||
throw syntaxError("Unterminated string");
|
throw syntaxError("Unterminated string");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void skipUnquotedValue() throws IOException {
|
private void skipUnquotedValue() throws IOException {
|
||||||
do {
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; pos + i < limit; i++) {
|
|
||||||
switch (buffer[pos + i]) {
|
findNonStringChar:
|
||||||
|
for (; fillBuffer(i + 1); i++) {
|
||||||
|
switch (buffer.getByte(i)) {
|
||||||
case '/':
|
case '/':
|
||||||
case '\\':
|
case '\\':
|
||||||
case ';':
|
case ';':
|
||||||
@@ -1141,12 +1055,11 @@ public class JsonReader implements Closeable {
|
|||||||
case '\f':
|
case '\f':
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
pos += i;
|
break findNonStringChar;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos += i;
|
|
||||||
} while (fillBuffer(1));
|
buffer.skip(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1170,7 +1083,7 @@ public class JsonReader implements Closeable {
|
|||||||
result = (int) peekedLong;
|
result = (int) peekedLong;
|
||||||
if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
|
if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
|
||||||
throw new NumberFormatException("Expected an int but was " + peekedLong
|
throw new NumberFormatException("Expected an int but was " + peekedLong
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
pathIndices[stackSize - 1]++;
|
pathIndices[stackSize - 1]++;
|
||||||
@@ -1178,8 +1091,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p == PEEKED_NUMBER) {
|
if (p == PEEKED_NUMBER) {
|
||||||
peekedString = new String(buffer, pos, peekedNumberLength);
|
peekedString = buffer.readUtf8(peekedNumberLength);
|
||||||
pos += peekedNumberLength;
|
|
||||||
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
|
||||||
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
|
||||||
try {
|
try {
|
||||||
@@ -1192,7 +1104,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected an int but was " + peek()
|
throw new IllegalStateException("Expected an int but was " + peek()
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
peeked = PEEKED_BUFFERED;
|
peeked = PEEKED_BUFFERED;
|
||||||
@@ -1200,7 +1112,7 @@ public class JsonReader implements Closeable {
|
|||||||
result = (int) asDouble;
|
result = (int) asDouble;
|
||||||
if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
|
if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
|
||||||
throw new NumberFormatException("Expected an int but was " + peekedString
|
throw new NumberFormatException("Expected an int but was " + peekedString
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
+ " at path " + getPath());
|
||||||
}
|
}
|
||||||
peekedString = null;
|
peekedString = null;
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
@@ -1215,7 +1127,8 @@ public class JsonReader implements Closeable {
|
|||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
stack[0] = JsonScope.CLOSED;
|
stack[0] = JsonScope.CLOSED;
|
||||||
stackSize = 1;
|
stackSize = 1;
|
||||||
in.close();
|
buffer.clear();
|
||||||
|
source.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1250,7 +1163,7 @@ public class JsonReader implements Closeable {
|
|||||||
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
|
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
|
||||||
skipQuotedValue('"');
|
skipQuotedValue('"');
|
||||||
} else if (p == PEEKED_NUMBER) {
|
} else if (p == PEEKED_NUMBER) {
|
||||||
pos += peekedNumberLength;
|
buffer.skip(peekedNumberLength);
|
||||||
}
|
}
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} while (count != 0);
|
} while (count != 0);
|
||||||
@@ -1280,41 +1193,11 @@ public class JsonReader implements Closeable {
|
|||||||
* false.
|
* false.
|
||||||
*/
|
*/
|
||||||
private boolean fillBuffer(int minimum) throws IOException {
|
private boolean fillBuffer(int minimum) throws IOException {
|
||||||
char[] buffer = this.buffer;
|
while (buffer.size() < minimum) {
|
||||||
lineStart -= pos;
|
if (source.read(buffer, 2048) == -1) return false;
|
||||||
if (limit != pos) {
|
|
||||||
limit -= pos;
|
|
||||||
System.arraycopy(buffer, pos, buffer, 0, limit);
|
|
||||||
} else {
|
|
||||||
limit = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
int total;
|
|
||||||
while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
|
|
||||||
limit += total;
|
|
||||||
|
|
||||||
// if this is the first read, consume an optional byte order mark (BOM) if it exists
|
|
||||||
if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
|
|
||||||
pos++;
|
|
||||||
lineStart++;
|
|
||||||
minimum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (limit >= minimum) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getLineNumber() {
|
|
||||||
return lineNumber + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getColumnNumber() {
|
|
||||||
return pos - lineStart + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next character in the stream that is neither whitespace nor a
|
* Returns the next character in the stream that is neither whitespace nor a
|
||||||
@@ -1331,65 +1214,46 @@ public class JsonReader implements Closeable {
|
|||||||
* before any (potentially indirect) call to fillBuffer() and reread both
|
* before any (potentially indirect) call to fillBuffer() and reread both
|
||||||
* 'p' and 'l' after any (potentially indirect) call to the same method.
|
* 'p' and 'l' after any (potentially indirect) call to the same method.
|
||||||
*/
|
*/
|
||||||
char[] buffer = this.buffer;
|
int p = 0;
|
||||||
int p = pos;
|
while (fillBuffer(p + 1)) {
|
||||||
int l = limit;
|
int c = buffer.getByte(p++);
|
||||||
while (true) {
|
if (c == '\n' || c == ' ' || c == '\r' || c == '\t') {
|
||||||
if (p == l) {
|
|
||||||
pos = p;
|
|
||||||
if (!fillBuffer(1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p = pos;
|
|
||||||
l = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
int c = buffer[p++];
|
|
||||||
if (c == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
lineStart = p;
|
|
||||||
continue;
|
|
||||||
} else if (c == ' ' || c == '\r' || c == '\t') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer.skip(p - 1);
|
||||||
if (c == '/') {
|
if (c == '/') {
|
||||||
pos = p;
|
if (!fillBuffer(2)) {
|
||||||
if (p == l) {
|
|
||||||
pos--; // push back '/' so it's still in the buffer when this method returns
|
|
||||||
boolean charsLoaded = fillBuffer(2);
|
|
||||||
pos++; // consume the '/' again
|
|
||||||
if (!charsLoaded) {
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
checkLenient();
|
checkLenient();
|
||||||
char peek = buffer[pos];
|
byte peek = buffer.getByte(1);
|
||||||
switch (peek) {
|
switch (peek) {
|
||||||
case '*':
|
case '*':
|
||||||
// skip a /* c-style comment */
|
// skip a /* c-style comment */
|
||||||
pos++;
|
buffer.readByte(); // '/'
|
||||||
|
buffer.readByte(); // '*'
|
||||||
if (!skipTo("*/")) {
|
if (!skipTo("*/")) {
|
||||||
throw syntaxError("Unterminated comment");
|
throw syntaxError("Unterminated comment");
|
||||||
}
|
}
|
||||||
p = pos + 2;
|
buffer.readByte(); // '*'
|
||||||
l = limit;
|
buffer.readByte(); // '/'
|
||||||
|
p = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
// skip a // end-of-line comment
|
// skip a // end-of-line comment
|
||||||
pos++;
|
buffer.readByte(); // '/'
|
||||||
|
buffer.readByte(); // '/'
|
||||||
skipToEndOfLine();
|
skipToEndOfLine();
|
||||||
p = pos;
|
p = 0;
|
||||||
l = limit;
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
} else if (c == '#') {
|
} else if (c == '#') {
|
||||||
pos = p;
|
|
||||||
/*
|
/*
|
||||||
* Skip a # hash end-of-line comment. The JSON RFC doesn't
|
* Skip a # hash end-of-line comment. The JSON RFC doesn't
|
||||||
* specify this behaviour, but it's required to parse
|
* specify this behaviour, but it's required to parse
|
||||||
@@ -1397,16 +1261,13 @@ public class JsonReader implements Closeable {
|
|||||||
*/
|
*/
|
||||||
checkLenient();
|
checkLenient();
|
||||||
skipToEndOfLine();
|
skipToEndOfLine();
|
||||||
p = pos;
|
p = 0;
|
||||||
l = limit;
|
|
||||||
} else {
|
} else {
|
||||||
pos = p;
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (throwOnEof) {
|
if (throwOnEof) {
|
||||||
throw new EOFException("End of input"
|
throw new EOFException("End of input");
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber());
|
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1424,13 +1285,9 @@ public class JsonReader implements Closeable {
|
|||||||
* caller.
|
* caller.
|
||||||
*/
|
*/
|
||||||
private void skipToEndOfLine() throws IOException {
|
private void skipToEndOfLine() throws IOException {
|
||||||
while (pos < limit || fillBuffer(1)) {
|
while (fillBuffer(1)) {
|
||||||
char c = buffer[pos++];
|
byte c = buffer.readByte();
|
||||||
if (c == '\n') {
|
if (c == '\n' || c == '\r') {
|
||||||
lineNumber++;
|
|
||||||
lineStart = pos;
|
|
||||||
break;
|
|
||||||
} else if (c == '\r') {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1441,14 +1298,10 @@ public class JsonReader implements Closeable {
|
|||||||
*/
|
*/
|
||||||
private boolean skipTo(String toFind) throws IOException {
|
private boolean skipTo(String toFind) throws IOException {
|
||||||
outer:
|
outer:
|
||||||
for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
|
for (; fillBuffer(toFind.length());) {
|
||||||
if (buffer[pos] == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
lineStart = pos + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int c = 0; c < toFind.length(); c++) {
|
for (int c = 0; c < toFind.length(); c++) {
|
||||||
if (buffer[pos + c] != toFind.charAt(c)) {
|
if (buffer.getByte(c) != toFind.charAt(c)) {
|
||||||
|
buffer.readByte();
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1458,8 +1311,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public String toString() {
|
@Override public String toString() {
|
||||||
return getClass().getSimpleName()
|
return getClass().getSimpleName();
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1503,20 +1355,20 @@ public class JsonReader implements Closeable {
|
|||||||
* malformed.
|
* malformed.
|
||||||
*/
|
*/
|
||||||
private char readEscapeCharacter() throws IOException {
|
private char readEscapeCharacter() throws IOException {
|
||||||
if (pos == limit && !fillBuffer(1)) {
|
if (!fillBuffer(1)) {
|
||||||
throw syntaxError("Unterminated escape sequence");
|
throw syntaxError("Unterminated escape sequence");
|
||||||
}
|
}
|
||||||
|
|
||||||
char escaped = buffer[pos++];
|
byte escaped = buffer.readByte();
|
||||||
switch (escaped) {
|
switch (escaped) {
|
||||||
case 'u':
|
case 'u':
|
||||||
if (pos + 4 > limit && !fillBuffer(4)) {
|
if (!fillBuffer(4)) {
|
||||||
throw syntaxError("Unterminated escape sequence");
|
throw syntaxError("Unterminated escape sequence");
|
||||||
}
|
}
|
||||||
// Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
|
// Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
|
||||||
char result = 0;
|
char result = 0;
|
||||||
for (int i = pos, end = i + 4; i < end; i++) {
|
for (int i = 0, end = i + 4; i < end; i++) {
|
||||||
char c = buffer[i];
|
byte c = buffer.getByte(i);
|
||||||
result <<= 4;
|
result <<= 4;
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
result += (c - '0');
|
result += (c - '0');
|
||||||
@@ -1525,10 +1377,10 @@ public class JsonReader implements Closeable {
|
|||||||
} else if (c >= 'A' && c <= 'F') {
|
} else if (c >= 'A' && c <= 'F') {
|
||||||
result += (c - 'A' + 10);
|
result += (c - 'A' + 10);
|
||||||
} else {
|
} else {
|
||||||
throw new NumberFormatException("\\u" + new String(buffer, pos, 4));
|
throw new NumberFormatException("\\u" + buffer.readUtf8(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos += 4;
|
buffer.skip(4);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
@@ -1547,15 +1399,11 @@ public class JsonReader implements Closeable {
|
|||||||
return '\f';
|
return '\f';
|
||||||
|
|
||||||
case '\n':
|
case '\n':
|
||||||
lineNumber++;
|
|
||||||
lineStart = pos;
|
|
||||||
// fall-through
|
|
||||||
|
|
||||||
case '\'':
|
case '\'':
|
||||||
case '"':
|
case '"':
|
||||||
case '\\':
|
case '\\':
|
||||||
default:
|
default:
|
||||||
return escaped;
|
return (char) escaped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1564,29 +1412,6 @@ public class JsonReader implements Closeable {
|
|||||||
* with this reader's content.
|
* with this reader's content.
|
||||||
*/
|
*/
|
||||||
private IOException syntaxError(String message) throws IOException {
|
private IOException syntaxError(String message) throws IOException {
|
||||||
throw new IOException(message
|
throw new IOException(message + " at path " + getPath());
|
||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consumes the non-execute prefix if it exists.
|
|
||||||
*/
|
|
||||||
private void consumeNonExecutePrefix() throws IOException {
|
|
||||||
// fast forward through the leading whitespace
|
|
||||||
nextNonWhitespace(true);
|
|
||||||
pos--;
|
|
||||||
|
|
||||||
if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
|
|
||||||
if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
|
|
||||||
return; // not a security token!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we consumed a security token!
|
|
||||||
pos += NON_EXECUTE_PREFIX.length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -138,7 +138,6 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
* error. http://code.google.com/p/google-gson/issues/detail?id=341
|
* error. http://code.google.com/p/google-gson/issues/detail?id=341
|
||||||
*/
|
*/
|
||||||
private static final String[] REPLACEMENT_CHARS;
|
private static final String[] REPLACEMENT_CHARS;
|
||||||
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
|
|
||||||
static {
|
static {
|
||||||
REPLACEMENT_CHARS = new String[128];
|
REPLACEMENT_CHARS = new String[128];
|
||||||
for (int i = 0; i <= 0x1f; i++) {
|
for (int i = 0; i <= 0x1f; i++) {
|
||||||
@@ -151,12 +150,6 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
REPLACEMENT_CHARS['\n'] = "\\n";
|
REPLACEMENT_CHARS['\n'] = "\\n";
|
||||||
REPLACEMENT_CHARS['\r'] = "\\r";
|
REPLACEMENT_CHARS['\r'] = "\\r";
|
||||||
REPLACEMENT_CHARS['\f'] = "\\f";
|
REPLACEMENT_CHARS['\f'] = "\\f";
|
||||||
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
|
|
||||||
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
|
|
||||||
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
|
|
||||||
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
|
|
||||||
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
|
|
||||||
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The output data, containing at most one top-level array or object. */
|
/** The output data, containing at most one top-level array or object. */
|
||||||
@@ -181,11 +174,9 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
|
|
||||||
private boolean lenient;
|
private boolean lenient;
|
||||||
|
|
||||||
private boolean htmlSafe;
|
|
||||||
|
|
||||||
private String deferredName;
|
private String deferredName;
|
||||||
|
|
||||||
private boolean serializeNulls = true;
|
private boolean serializeNulls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
|
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
|
||||||
@@ -240,25 +231,6 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
return lenient;
|
return lenient;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure this writer to emit JSON that's safe for direct inclusion in HTML
|
|
||||||
* and XML documents. This escapes the HTML characters {@code <}, {@code >},
|
|
||||||
* {@code &} and {@code =} before writing them to the stream. Without this
|
|
||||||
* setting, your XML/HTML encoder should replace these characters with the
|
|
||||||
* corresponding escape sequences.
|
|
||||||
*/
|
|
||||||
public final void setHtmlSafe(boolean htmlSafe) {
|
|
||||||
this.htmlSafe = htmlSafe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this writer writes JSON that's safe for inclusion in HTML
|
|
||||||
* and XML documents.
|
|
||||||
*/
|
|
||||||
public final boolean isHtmlSafe() {
|
|
||||||
return htmlSafe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether object members are serialized when their value is null.
|
* Sets whether object members are serialized when their value is null.
|
||||||
* This has no impact on array elements. The default is true.
|
* This has no impact on array elements. The default is true.
|
||||||
@@ -528,7 +500,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void string(String value) throws IOException {
|
private void string(String value) throws IOException {
|
||||||
String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
|
String[] replacements = REPLACEMENT_CHARS;
|
||||||
out.write("\"");
|
out.write("\"");
|
||||||
int last = 0;
|
int last = 0;
|
||||||
int length = value.length();
|
int length = value.length();
|
||||||
|
@@ -16,13 +16,13 @@
|
|||||||
package com.squareup.moshi;
|
package com.squareup.moshi;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import org.junit.Test;
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
public class JsonReaderPathTest extends TestCase {
|
import static org.junit.Assert.assertEquals;
|
||||||
public void testPath() throws IOException {
|
|
||||||
JsonReader reader = new JsonReader(
|
public class JsonReaderPathTest {
|
||||||
new StringReader("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}"));
|
@Test public void path() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}");
|
||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
assertEquals("$.", reader.getPath());
|
assertEquals("$.", reader.getPath());
|
||||||
@@ -60,8 +60,8 @@ public class JsonReaderPathTest extends TestCase {
|
|||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testObjectPath() throws IOException {
|
@Test public void objectPath() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("{\"a\":1,\"b\":2}"));
|
JsonReader reader = new JsonReader("{\"a\":1,\"b\":2}");
|
||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
|
|
||||||
reader.peek();
|
reader.peek();
|
||||||
@@ -100,8 +100,8 @@ public class JsonReaderPathTest extends TestCase {
|
|||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testArrayPath() throws IOException {
|
@Test public void arrayPath() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[1,2]"));
|
JsonReader reader = new JsonReader("[1,2]");
|
||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
|
|
||||||
reader.peek();
|
reader.peek();
|
||||||
@@ -130,8 +130,8 @@ public class JsonReaderPathTest extends TestCase {
|
|||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultipleTopLevelValuesInOneDocument() throws IOException {
|
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[][]"));
|
JsonReader reader = new JsonReader("[][]");
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
reader.endArray();
|
reader.endArray();
|
||||||
@@ -141,23 +141,23 @@ public class JsonReaderPathTest extends TestCase {
|
|||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipArrayElements() throws IOException {
|
@Test public void skipArrayElements() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[1,2,3]"));
|
JsonReader reader = new JsonReader("[1,2,3]");
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
assertEquals("$[2]", reader.getPath());
|
assertEquals("$[2]", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipObjectNames() throws IOException {
|
@Test public void skipObjectNames() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("{\"a\":1}"));
|
JsonReader reader = new JsonReader("{\"a\":1}");
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
assertEquals("$.null", reader.getPath());
|
assertEquals("$.null", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipObjectValues() throws IOException {
|
@Test public void skipObjectValues() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("{\"a\":1,\"b\":2}"));
|
JsonReader reader = new JsonReader("{\"a\":1,\"b\":2}");
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
reader.nextName();
|
reader.nextName();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
@@ -166,8 +166,8 @@ public class JsonReaderPathTest extends TestCase {
|
|||||||
assertEquals("$.b", reader.getPath());
|
assertEquals("$.b", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipNestedStructures() throws IOException {
|
@Test public void skipNestedStructures() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[[1,2,3],4]"));
|
JsonReader reader = new JsonReader("[[1,2,3],4]");
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
assertEquals("$[1]", reader.getPath());
|
assertEquals("$[1]", reader.getPath());
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,36 @@ import java.io.IOException;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
import static org.junit.Assert.assertEquals;
|
||||||
public final class JsonWriterTest extends TestCase {
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public void testWrongTopLevelType() throws IOException {
|
public final class JsonWriterTest {
|
||||||
|
@Test public void nullsValuesNotSerializedByDefault() throws IOException {
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
|
jsonWriter.beginObject();
|
||||||
|
jsonWriter.name("a");
|
||||||
|
jsonWriter.nullValue();
|
||||||
|
jsonWriter.endObject();
|
||||||
|
jsonWriter.close();
|
||||||
|
assertEquals("{}", stringWriter.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void nullsValuesSerializedWhenConfigured() throws IOException {
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
|
jsonWriter.setSerializeNulls(true);
|
||||||
|
jsonWriter.beginObject();
|
||||||
|
jsonWriter.name("a");
|
||||||
|
jsonWriter.nullValue();
|
||||||
|
jsonWriter.endObject();
|
||||||
|
jsonWriter.close();
|
||||||
|
assertEquals("{\"a\":null}", stringWriter.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void wrongTopLevelType() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
try {
|
try {
|
||||||
@@ -34,7 +58,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testTwoNames() throws IOException {
|
@Test public void twoNames() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -46,7 +70,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNameWithoutValue() throws IOException {
|
@Test public void nameWithoutValue() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -58,7 +82,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testValueWithoutName() throws IOException {
|
@Test public void valueWithoutName() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -69,7 +93,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultipleTopLevelValues() throws IOException {
|
@Test public void multipleTopLevelValues() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray().endArray();
|
jsonWriter.beginArray().endArray();
|
||||||
@@ -80,7 +104,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBadNestingObject() throws IOException {
|
@Test public void badNestingObject() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -92,7 +116,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBadNestingArray() throws IOException {
|
@Test public void badNestingArray() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -104,7 +128,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullName() throws IOException {
|
@Test public void nullName() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -115,9 +139,10 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullStringValue() throws IOException {
|
@Test public void nullStringValue() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
|
jsonWriter.setSerializeNulls(true);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
jsonWriter.name("a");
|
jsonWriter.name("a");
|
||||||
jsonWriter.value((String) null);
|
jsonWriter.value((String) null);
|
||||||
@@ -125,7 +150,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("{\"a\":null}", stringWriter.toString());
|
assertEquals("{\"a\":null}", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNonFiniteDoubles() throws IOException {
|
@Test public void nonFiniteDoubles() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -146,7 +171,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNonFiniteBoxedDoubles() throws IOException {
|
@Test public void nonFiniteBoxedDoubles() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -167,7 +192,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDoubles() throws IOException {
|
@Test public void doubles() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -193,7 +218,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "2.718281828459045]", stringWriter.toString());
|
+ "2.718281828459045]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLongs() throws IOException {
|
@Test public void longs() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -211,7 +236,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "9223372036854775807]", stringWriter.toString());
|
+ "9223372036854775807]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNumbers() throws IOException {
|
@Test public void numbers() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -227,7 +252,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "3.141592653589793238462643383]", stringWriter.toString());
|
+ "3.141592653589793238462643383]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBooleans() throws IOException {
|
@Test public void booleans() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -237,7 +262,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[true,false]", stringWriter.toString());
|
assertEquals("[true,false]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNulls() throws IOException {
|
@Test public void nulls() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -246,7 +271,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[null]", stringWriter.toString());
|
assertEquals("[null]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStrings() throws IOException {
|
@Test public void strings() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -289,7 +314,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "\"\\u0019\"]", stringWriter.toString());
|
+ "\"\\u0019\"]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUnicodeLineBreaksEscaped() throws IOException {
|
@Test public void unicodeLineBreaksEscaped() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -298,7 +323,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString());
|
assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyArray() throws IOException {
|
@Test public void emptyArray() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -306,7 +331,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[]", stringWriter.toString());
|
assertEquals("[]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyObject() throws IOException {
|
@Test public void emptyObject() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -314,7 +339,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("{}", stringWriter.toString());
|
assertEquals("{}", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testObjectsInArrays() throws IOException {
|
@Test public void objectsInArrays() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginArray();
|
jsonWriter.beginArray();
|
||||||
@@ -331,7 +356,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "{\"c\":6,\"d\":true}]", stringWriter.toString());
|
+ "{\"c\":6,\"d\":true}]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testArraysInObjects() throws IOException {
|
@Test public void arraysInObjects() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -350,7 +375,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "\"b\":[6,true]}", stringWriter.toString());
|
+ "\"b\":[6,true]}", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDeepNestingArrays() throws IOException {
|
@Test public void deepNestingArrays() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
@@ -362,7 +387,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
|
assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDeepNestingObjects() throws IOException {
|
@Test public void deepNestingObjects() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -379,7 +404,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
+ "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
|
+ "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRepeatedName() throws IOException {
|
@Test public void repeatedName() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -390,9 +415,10 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
|
assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrettyPrintObject() throws IOException {
|
@Test public void prettyPrintObject() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
|
jsonWriter.setSerializeNulls(true);
|
||||||
jsonWriter.setIndent(" ");
|
jsonWriter.setIndent(" ");
|
||||||
|
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
@@ -427,7 +453,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals(expected, stringWriter.toString());
|
assertEquals(expected, stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrettyPrintArray() throws IOException {
|
@Test public void prettyPrintArray() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
jsonWriter.setIndent(" ");
|
jsonWriter.setIndent(" ");
|
||||||
@@ -464,7 +490,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals(expected, stringWriter.toString());
|
assertEquals(expected, stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException {
|
@Test public void lenientWriterPermitsMultipleTopLevelValues() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.setLenient(true);
|
writer.setLenient(true);
|
||||||
@@ -476,7 +502,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
assertEquals("[][]", stringWriter.toString());
|
assertEquals("[][]", stringWriter.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
|
@Test public void strictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -488,7 +514,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClosedWriterThrowsOnStructure() throws IOException {
|
@Test public void closedWriterThrowsOnStructure() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -516,7 +542,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClosedWriterThrowsOnName() throws IOException {
|
@Test public void closedWriterThrowsOnName() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -529,7 +555,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClosedWriterThrowsOnValue() throws IOException {
|
@Test public void closedWriterThrowsOnValue() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -542,7 +568,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClosedWriterThrowsOnFlush() throws IOException {
|
@Test public void closedWriterThrowsOnFlush() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -555,7 +581,7 @@ public final class JsonWriterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWriterCloseIsIdempotent() throws IOException {
|
@Test public void writerCloseIsIdempotent() throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
JsonWriter writer = new JsonWriter(stringWriter);
|
JsonWriter writer = new JsonWriter(stringWriter);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
|
Reference in New Issue
Block a user