mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Promote stack management to the JsonReader/JsonWriter supertypes.
It turns out that we can reuse a lot of code with inheritance. Who knew?
This commit is contained in:
@@ -57,35 +57,11 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
/** The output data, containing at most one top-level array or object. */
|
/** The output data, containing at most one top-level array or object. */
|
||||||
private final BufferedSink sink;
|
private final BufferedSink sink;
|
||||||
|
|
||||||
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
|
/** The name/value separator; either ":" or ": ". */
|
||||||
// up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
|
|
||||||
// StackOverflowErrors.
|
|
||||||
private final int[] stack = new int[32];
|
|
||||||
private int stackSize = 0;
|
|
||||||
{
|
|
||||||
push(EMPTY_DOCUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String[] pathNames = new String[32];
|
|
||||||
private final int[] pathIndices = new int[32];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string containing a full set of spaces for a single level of
|
|
||||||
* indentation, or null for no pretty printing.
|
|
||||||
*/
|
|
||||||
private String indent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name/value separator; either ":" or ": ".
|
|
||||||
*/
|
|
||||||
private String separator = ":";
|
private String separator = ":";
|
||||||
|
|
||||||
private boolean lenient;
|
|
||||||
|
|
||||||
private String deferredName;
|
private String deferredName;
|
||||||
|
|
||||||
private boolean serializeNulls;
|
|
||||||
|
|
||||||
private boolean promoteNameToValue;
|
private boolean promoteNameToValue;
|
||||||
|
|
||||||
BufferedSinkJsonWriter(BufferedSink sink) {
|
BufferedSinkJsonWriter(BufferedSink sink) {
|
||||||
@@ -93,36 +69,12 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
throw new NullPointerException("sink == null");
|
throw new NullPointerException("sink == null");
|
||||||
}
|
}
|
||||||
this.sink = sink;
|
this.sink = sink;
|
||||||
|
pushScope(EMPTY_DOCUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public final void setIndent(String indent) {
|
@Override public void setIndent(String indent) {
|
||||||
if (indent.length() == 0) {
|
super.setIndent(indent);
|
||||||
this.indent = null;
|
this.separator = !indent.isEmpty() ? ": " : ":";
|
||||||
this.separator = ":";
|
|
||||||
} else {
|
|
||||||
this.indent = indent;
|
|
||||||
this.separator = ": ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final String getIndent() {
|
|
||||||
return indent != null ? indent : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isLenient() {
|
|
||||||
return lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final void setSerializeNulls(boolean serializeNulls) {
|
|
||||||
this.serializeNulls = serializeNulls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final boolean getSerializeNulls() {
|
|
||||||
return serializeNulls;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter beginArray() throws IOException {
|
@Override public JsonWriter beginArray() throws IOException {
|
||||||
@@ -150,7 +102,7 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
*/
|
*/
|
||||||
private JsonWriter open(int empty, String openBracket) throws IOException {
|
private JsonWriter open(int empty, String openBracket) throws IOException {
|
||||||
beforeValue();
|
beforeValue();
|
||||||
push(empty);
|
pushScope(empty);
|
||||||
pathIndices[stackSize - 1] = 0;
|
pathIndices[stackSize - 1] = 0;
|
||||||
sink.writeUtf8(openBracket);
|
sink.writeUtf8(openBracket);
|
||||||
return this;
|
return this;
|
||||||
@@ -160,9 +112,8 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
* Closes the current scope by appending any necessary whitespace and the
|
* Closes the current scope by appending any necessary whitespace and the
|
||||||
* given bracket.
|
* given bracket.
|
||||||
*/
|
*/
|
||||||
private JsonWriter close(int empty, int nonempty, String closeBracket)
|
private JsonWriter close(int empty, int nonempty, String closeBracket) throws IOException {
|
||||||
throws IOException {
|
int context = peekScope();
|
||||||
int context = peek();
|
|
||||||
if (context != nonempty && context != empty) {
|
if (context != nonempty && context != empty) {
|
||||||
throw new IllegalStateException("Nesting problem.");
|
throw new IllegalStateException("Nesting problem.");
|
||||||
}
|
}
|
||||||
@@ -180,30 +131,6 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void push(int newTop) {
|
|
||||||
if (stackSize == stack.length) {
|
|
||||||
throw new JsonDataException("Nesting too deep at " + getPath() + ": circular reference?");
|
|
||||||
}
|
|
||||||
stack[stackSize++] = newTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the scope on the top of the stack.
|
|
||||||
*/
|
|
||||||
private int peek() {
|
|
||||||
if (stackSize == 0) {
|
|
||||||
throw new IllegalStateException("JsonWriter is closed.");
|
|
||||||
}
|
|
||||||
return stack[stackSize - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace the value on the top of the stack with the given value.
|
|
||||||
*/
|
|
||||||
private void replaceTop(int topOfStack) {
|
|
||||||
stack[stackSize - 1] = topOfStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public JsonWriter name(String name) throws IOException {
|
@Override public JsonWriter name(String name) throws IOException {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
throw new NullPointerException("name == null");
|
throw new NullPointerException("name == null");
|
||||||
@@ -337,7 +264,7 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
sink.close();
|
sink.close();
|
||||||
|
|
||||||
int size = stackSize;
|
int size = stackSize;
|
||||||
if (size > 1 || size == 1 && stack[size - 1] != NONEMPTY_DOCUMENT) {
|
if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) {
|
||||||
throw new IOException("Incomplete document");
|
throw new IOException("Incomplete document");
|
||||||
}
|
}
|
||||||
stackSize = 0;
|
stackSize = 0;
|
||||||
@@ -395,7 +322,7 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
* adjusts the stack to expect the name's value.
|
* adjusts the stack to expect the name's value.
|
||||||
*/
|
*/
|
||||||
private void beforeName() throws IOException {
|
private void beforeName() throws IOException {
|
||||||
int context = peek();
|
int context = peekScope();
|
||||||
if (context == NONEMPTY_OBJECT) { // first in object
|
if (context == NONEMPTY_OBJECT) { // first in object
|
||||||
sink.writeByte(',');
|
sink.writeByte(',');
|
||||||
} else if (context != EMPTY_OBJECT) { // not in an object!
|
} else if (context != EMPTY_OBJECT) { // not in an object!
|
||||||
@@ -412,7 +339,7 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("fallthrough")
|
@SuppressWarnings("fallthrough")
|
||||||
private void beforeValue() throws IOException {
|
private void beforeValue() throws IOException {
|
||||||
switch (peek()) {
|
switch (peekScope()) {
|
||||||
case NONEMPTY_DOCUMENT:
|
case NONEMPTY_DOCUMENT:
|
||||||
if (!lenient) {
|
if (!lenient) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
@@ -444,14 +371,10 @@ final class BufferedSinkJsonWriter extends JsonWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override void promoteNameToValue() throws IOException {
|
@Override void promoteNameToValue() throws IOException {
|
||||||
int context = peek();
|
int context = peekScope();
|
||||||
if (context != NONEMPTY_OBJECT && context != EMPTY_OBJECT) {
|
if (context != NONEMPTY_OBJECT && context != EMPTY_OBJECT) {
|
||||||
throw new IllegalStateException("Nesting problem.");
|
throw new IllegalStateException("Nesting problem.");
|
||||||
}
|
}
|
||||||
promoteNameToValue = true;
|
promoteNameToValue = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String getPath() {
|
|
||||||
return JsonScope.getPath(stackSize, stack, pathNames, pathIndices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -63,12 +63,6 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
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;
|
||||||
|
|
||||||
/** True to accept non-spec compliant JSON */
|
|
||||||
private boolean lenient = false;
|
|
||||||
|
|
||||||
/** True to throw a {@link JsonDataException} on any attempt to call {@link #skipValue()}. */
|
|
||||||
private boolean failOnUnknown = false;
|
|
||||||
|
|
||||||
/** The input JSON. */
|
/** The input JSON. */
|
||||||
private final BufferedSource source;
|
private final BufferedSource source;
|
||||||
private final Buffer buffer;
|
private final Buffer buffer;
|
||||||
@@ -94,41 +88,13 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
*/
|
*/
|
||||||
private String peekedString;
|
private String peekedString;
|
||||||
|
|
||||||
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
|
|
||||||
// up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
|
|
||||||
// StackOverflowErrors.
|
|
||||||
private final int[] stack = new int[32];
|
|
||||||
private int stackSize = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String[] pathNames = new String[32];
|
|
||||||
private final int[] pathIndices = new int[32];
|
|
||||||
|
|
||||||
BufferedSourceJsonReader(BufferedSource source) {
|
BufferedSourceJsonReader(BufferedSource source) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
throw new NullPointerException("source == null");
|
throw new NullPointerException("source == null");
|
||||||
}
|
}
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.buffer = source.buffer();
|
this.buffer = source.buffer();
|
||||||
}
|
pushScope(JsonScope.EMPTY_DOCUMENT);
|
||||||
|
|
||||||
@Override public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isLenient() {
|
|
||||||
return lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setFailOnUnknown(boolean failOnUnknown) {
|
|
||||||
this.failOnUnknown = failOnUnknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean failOnUnknown() {
|
|
||||||
return failOnUnknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void beginArray() throws IOException {
|
@Override public void beginArray() throws IOException {
|
||||||
@@ -137,7 +103,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
p = doPeek();
|
p = doPeek();
|
||||||
}
|
}
|
||||||
if (p == PEEKED_BEGIN_ARRAY) {
|
if (p == PEEKED_BEGIN_ARRAY) {
|
||||||
push(JsonScope.EMPTY_ARRAY);
|
pushScope(JsonScope.EMPTY_ARRAY);
|
||||||
pathIndices[stackSize - 1] = 0;
|
pathIndices[stackSize - 1] = 0;
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
@@ -167,7 +133,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
p = doPeek();
|
p = doPeek();
|
||||||
}
|
}
|
||||||
if (p == PEEKED_BEGIN_OBJECT) {
|
if (p == PEEKED_BEGIN_OBJECT) {
|
||||||
push(JsonScope.EMPTY_OBJECT);
|
pushScope(JsonScope.EMPTY_OBJECT);
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} else {
|
} else {
|
||||||
throw new JsonDataException("Expected BEGIN_OBJECT but was " + peek()
|
throw new JsonDataException("Expected BEGIN_OBJECT but was " + peek()
|
||||||
@@ -240,9 +206,9 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int doPeek() throws IOException {
|
private int doPeek() throws IOException {
|
||||||
int peekStack = stack[stackSize - 1];
|
int peekStack = scopes[stackSize - 1];
|
||||||
if (peekStack == JsonScope.EMPTY_ARRAY) {
|
if (peekStack == JsonScope.EMPTY_ARRAY) {
|
||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
|
scopes[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
|
||||||
} 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);
|
||||||
@@ -258,7 +224,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
throw syntaxError("Unterminated array");
|
throw syntaxError("Unterminated array");
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
|
} else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
|
||||||
stack[stackSize - 1] = JsonScope.DANGLING_NAME;
|
scopes[stackSize - 1] = JsonScope.DANGLING_NAME;
|
||||||
// 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);
|
||||||
@@ -299,7 +265,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.DANGLING_NAME) {
|
} else if (peekStack == JsonScope.DANGLING_NAME) {
|
||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
|
scopes[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 ':'.
|
buffer.readByte(); // Consume ':'.
|
||||||
@@ -316,7 +282,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
throw syntaxError("Expected ':'");
|
throw syntaxError("Expected ':'");
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
|
scopes[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);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
@@ -923,7 +889,7 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
|
|
||||||
@Override public void close() throws IOException {
|
@Override public void close() throws IOException {
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
stack[0] = JsonScope.CLOSED;
|
scopes[0] = JsonScope.CLOSED;
|
||||||
stackSize = 1;
|
stackSize = 1;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
source.close();
|
source.close();
|
||||||
@@ -941,10 +907,10 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p == PEEKED_BEGIN_ARRAY) {
|
if (p == PEEKED_BEGIN_ARRAY) {
|
||||||
push(JsonScope.EMPTY_ARRAY);
|
pushScope(JsonScope.EMPTY_ARRAY);
|
||||||
count++;
|
count++;
|
||||||
} else if (p == PEEKED_BEGIN_OBJECT) {
|
} else if (p == PEEKED_BEGIN_OBJECT) {
|
||||||
push(JsonScope.EMPTY_OBJECT);
|
pushScope(JsonScope.EMPTY_OBJECT);
|
||||||
count++;
|
count++;
|
||||||
} else if (p == PEEKED_END_ARRAY) {
|
} else if (p == PEEKED_END_ARRAY) {
|
||||||
stackSize--;
|
stackSize--;
|
||||||
@@ -968,13 +934,6 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
pathNames[stackSize - 1] = "null";
|
pathNames[stackSize - 1] = "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void push(int newTop) {
|
|
||||||
if (stackSize == stack.length) {
|
|
||||||
throw new JsonDataException("Nesting too deep at " + getPath());
|
|
||||||
}
|
|
||||||
stack[stackSize++] = newTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* part of a comment. When this returns, the returned character is always at
|
* part of a comment. When this returns, the returned character is always at
|
||||||
@@ -1083,10 +1042,6 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
return "JsonReader(" + source + ")";
|
return "JsonReader(" + source + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String getPath() {
|
|
||||||
return JsonScope.getPath(stackSize, stack, pathNames, pathIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unescapes the character identified by the character or characters that immediately follow a
|
* Unescapes the character identified by the character or characters that immediately follow a
|
||||||
* backslash. The backslash '\' should have already been read. This supports both unicode escapes
|
* backslash. The backslash '\' should have already been read. This supports both unicode escapes
|
||||||
@@ -1151,14 +1106,6 @@ final class BufferedSourceJsonReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws a new IO exception with the given message and a context snippet
|
|
||||||
* with this reader's content.
|
|
||||||
*/
|
|
||||||
private JsonEncodingException syntaxError(String message) throws JsonEncodingException {
|
|
||||||
throw new JsonEncodingException(message + " at path " + getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override void promoteNameToValue() throws IOException {
|
@Override void promoteNameToValue() throws IOException {
|
||||||
if (hasNext()) {
|
if (hasNext()) {
|
||||||
peekedString = nextName();
|
peekedString = nextName();
|
||||||
|
@@ -171,9 +171,21 @@ import okio.ByteString;
|
|||||||
* of this class are not thread safe.
|
* of this class are not thread safe.
|
||||||
*/
|
*/
|
||||||
public abstract class JsonReader implements Closeable {
|
public abstract class JsonReader implements Closeable {
|
||||||
/**
|
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
|
||||||
* Returns a new instance that reads a JSON-encoded stream from {@code source}.
|
// up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
|
||||||
*/
|
// StackOverflowErrors.
|
||||||
|
int stackSize = 0;
|
||||||
|
final int[] scopes = new int[32];
|
||||||
|
final String[] pathNames = new String[32];
|
||||||
|
final int[] pathIndices = new int[32];
|
||||||
|
|
||||||
|
/** True to accept non-spec compliant JSON */
|
||||||
|
boolean lenient;
|
||||||
|
|
||||||
|
/** True to throw a {@link JsonDataException} on any attempt to call {@link #skipValue()}. */
|
||||||
|
boolean failOnUnknown;
|
||||||
|
|
||||||
|
/** Returns a new instance that reads a JSON-encoded stream from {@code source}. */
|
||||||
public static JsonReader of(BufferedSource source) {
|
public static JsonReader of(BufferedSource source) {
|
||||||
return new BufferedSourceJsonReader(source);
|
return new BufferedSourceJsonReader(source);
|
||||||
}
|
}
|
||||||
@@ -182,6 +194,31 @@ public abstract class JsonReader implements Closeable {
|
|||||||
// Package-private to control subclasses.
|
// Package-private to control subclasses.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final void pushScope(int newTop) {
|
||||||
|
if (stackSize == scopes.length) {
|
||||||
|
throw new JsonDataException("Nesting too deep at " + getPath());
|
||||||
|
}
|
||||||
|
scopes[stackSize++] = newTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws a new IO exception with the given message and a context snippet
|
||||||
|
* with this reader's content.
|
||||||
|
*/
|
||||||
|
final JsonEncodingException syntaxError(String message) throws JsonEncodingException {
|
||||||
|
throw new JsonEncodingException(message + " at path " + getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonDataException typeMismatch(Object value, Object expected) {
|
||||||
|
if (value == null) {
|
||||||
|
return new JsonDataException(
|
||||||
|
"Expected " + expected + " but was null at path " + getPath());
|
||||||
|
} else {
|
||||||
|
return new JsonDataException("Expected " + expected + " but was " + value + ", a "
|
||||||
|
+ value.getClass().getName() + ", at path " + getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this parser to be liberal in what it accepts. By default
|
* Configure this parser to be liberal in what it accepts. By default
|
||||||
* this parser is strict and only accepts JSON as specified by <a
|
* this parser is strict and only accepts JSON as specified by <a
|
||||||
@@ -207,12 +244,16 @@ public abstract class JsonReader implements Closeable {
|
|||||||
* <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
|
* <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public abstract void setLenient(boolean lenient);
|
public final void setLenient(boolean lenient) {
|
||||||
|
this.lenient = lenient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this parser is liberal in what it accepts.
|
* Returns true if this parser is liberal in what it accepts.
|
||||||
*/
|
*/
|
||||||
public abstract boolean isLenient();
|
public final boolean isLenient() {
|
||||||
|
return lenient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure whether this parser throws a {@link JsonDataException} when {@link #skipValue} is
|
* Configure whether this parser throws a {@link JsonDataException} when {@link #skipValue} is
|
||||||
@@ -222,12 +263,16 @@ public abstract class JsonReader implements Closeable {
|
|||||||
* useful in development and debugging because it means a typo like "locatiom" will be detected
|
* useful in development and debugging because it means a typo like "locatiom" will be detected
|
||||||
* early. It's potentially harmful in production because it complicates revising a JSON schema.
|
* early. It's potentially harmful in production because it complicates revising a JSON schema.
|
||||||
*/
|
*/
|
||||||
public abstract void setFailOnUnknown(boolean failOnUnknown);
|
public final void setFailOnUnknown(boolean failOnUnknown) {
|
||||||
|
this.failOnUnknown = failOnUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this parser forbids skipping values.
|
* Returns true if this parser forbids skipping values.
|
||||||
*/
|
*/
|
||||||
public abstract boolean failOnUnknown();
|
public final boolean failOnUnknown() {
|
||||||
|
return failOnUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the next token from the JSON stream and asserts that it is the beginning of a new
|
* Consumes the next token from the JSON stream and asserts that it is the beginning of a new
|
||||||
@@ -349,7 +394,9 @@ public abstract class JsonReader implements Closeable {
|
|||||||
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
|
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
|
||||||
* the current location in the JSON value.
|
* the current location in the JSON value.
|
||||||
*/
|
*/
|
||||||
public abstract String getPath();
|
public final String getPath() {
|
||||||
|
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the reader to treat the next name as a string value. This is useful for map adapters so
|
* Changes the reader to treat the next name as a string value. This is useful for map adapters so
|
||||||
|
@@ -116,9 +116,23 @@ import okio.BufferedSink;
|
|||||||
* malformed JSON string will fail with an {@link IllegalStateException}.
|
* malformed JSON string will fail with an {@link IllegalStateException}.
|
||||||
*/
|
*/
|
||||||
public abstract class JsonWriter implements Closeable, Flushable {
|
public abstract class JsonWriter implements Closeable, Flushable {
|
||||||
|
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
|
||||||
|
// up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
|
||||||
|
// StackOverflowErrors.
|
||||||
|
int stackSize = 0;
|
||||||
|
final int[] scopes = new int[32];
|
||||||
|
final String[] pathNames = new String[32];
|
||||||
|
final int[] pathIndices = new int[32];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new instance that writes a JSON-encoded stream to {@code sink}.
|
* A string containing a full set of spaces for a single level of indentation, or null for no
|
||||||
|
* pretty printing.
|
||||||
*/
|
*/
|
||||||
|
String indent;
|
||||||
|
boolean lenient;
|
||||||
|
boolean serializeNulls;
|
||||||
|
|
||||||
|
/** Returns a new instance that writes a JSON-encoded stream to {@code sink}. */
|
||||||
public static JsonWriter of(BufferedSink sink) {
|
public static JsonWriter of(BufferedSink sink) {
|
||||||
return new BufferedSinkJsonWriter(sink);
|
return new BufferedSinkJsonWriter(sink);
|
||||||
}
|
}
|
||||||
@@ -127,6 +141,26 @@ public abstract class JsonWriter implements Closeable, Flushable {
|
|||||||
// Package-private to control subclasses.
|
// Package-private to control subclasses.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the scope on the top of the stack. */
|
||||||
|
final int peekScope() {
|
||||||
|
if (stackSize == 0) {
|
||||||
|
throw new IllegalStateException("JsonWriter is closed.");
|
||||||
|
}
|
||||||
|
return scopes[stackSize - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
final void pushScope(int newTop) {
|
||||||
|
if (stackSize == scopes.length) {
|
||||||
|
throw new JsonDataException("Nesting too deep at " + getPath() + ": circular reference?");
|
||||||
|
}
|
||||||
|
scopes[stackSize++] = newTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace the value on the top of the stack with the given value. */
|
||||||
|
final void replaceTop(int topOfStack) {
|
||||||
|
scopes[stackSize - 1] = topOfStack;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the indentation string to be repeated for each level of indentation
|
* Sets the indentation string to be repeated for each level of indentation
|
||||||
* in the encoded document. If {@code indent.isEmpty()} the encoded document
|
* in the encoded document. If {@code indent.isEmpty()} the encoded document
|
||||||
@@ -135,13 +169,17 @@ public abstract class JsonWriter implements Closeable, Flushable {
|
|||||||
*
|
*
|
||||||
* @param indent a string containing only whitespace.
|
* @param indent a string containing only whitespace.
|
||||||
*/
|
*/
|
||||||
public abstract void setIndent(String indent);
|
public void setIndent(String indent) {
|
||||||
|
this.indent = !indent.isEmpty() ? indent : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string containing only whitespace, used for each level of
|
* Returns a string containing only whitespace, used for each level of
|
||||||
* indentation. If empty, the encoded document will be compact.
|
* indentation. If empty, the encoded document will be compact.
|
||||||
*/
|
*/
|
||||||
public abstract String getIndent();
|
public final String getIndent() {
|
||||||
|
return indent != null ? indent : "";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this writer to relax its syntax rules. By default, this writer
|
* Configure this writer to relax its syntax rules. By default, this writer
|
||||||
@@ -155,24 +193,32 @@ public abstract class JsonWriter implements Closeable, Flushable {
|
|||||||
* Double#isInfinite() infinities}.
|
* Double#isInfinite() infinities}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public abstract void setLenient(boolean lenient);
|
public final void setLenient(boolean lenient) {
|
||||||
|
this.lenient = lenient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this writer has relaxed syntax rules.
|
* Returns true if this writer has relaxed syntax rules.
|
||||||
*/
|
*/
|
||||||
public abstract boolean isLenient();
|
public final boolean isLenient() {
|
||||||
|
return lenient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 false.
|
* This has no impact on array elements. The default is false.
|
||||||
*/
|
*/
|
||||||
public abstract void setSerializeNulls(boolean serializeNulls);
|
public final void setSerializeNulls(boolean serializeNulls) {
|
||||||
|
this.serializeNulls = serializeNulls;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if object members are serialized when their value is null.
|
* Returns true if object members are serialized when their value is null.
|
||||||
* This has no impact on array elements. The default is false.
|
* This has no impact on array elements. The default is false.
|
||||||
*/
|
*/
|
||||||
public abstract boolean getSerializeNulls();
|
public final boolean getSerializeNulls() {
|
||||||
|
return serializeNulls;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begins encoding a new array. Each call to this method must be paired with
|
* Begins encoding a new array. Each call to this method must be paired with
|
||||||
@@ -276,5 +322,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
|
|||||||
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
|
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
|
||||||
* the current location in the JSON value.
|
* the current location in the JSON value.
|
||||||
*/
|
*/
|
||||||
public abstract String getPath();
|
public final String getPath() {
|
||||||
|
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -48,35 +48,13 @@ final class ObjectJsonReader extends JsonReader {
|
|||||||
/** Sentinel object pushed on {@link #stack} when the reader is closed. */
|
/** Sentinel object pushed on {@link #stack} when the reader is closed. */
|
||||||
private static final Object JSON_READER_CLOSED = new Object();
|
private static final Object JSON_READER_CLOSED = new Object();
|
||||||
|
|
||||||
private int stackSize = 0;
|
|
||||||
private final Object[] stack = new Object[32];
|
private final Object[] stack = new Object[32];
|
||||||
private final int[] scopes = new int[32];
|
|
||||||
private final String[] pathNames = new String[32];
|
|
||||||
private final int[] pathIndices = new int[32];
|
|
||||||
private boolean lenient;
|
|
||||||
private boolean failOnUnknown;
|
|
||||||
|
|
||||||
public ObjectJsonReader(Object root) {
|
public ObjectJsonReader(Object root) {
|
||||||
scopes[stackSize] = JsonScope.NONEMPTY_DOCUMENT;
|
scopes[stackSize] = JsonScope.NONEMPTY_DOCUMENT;
|
||||||
stack[stackSize++] = root;
|
stack[stackSize++] = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isLenient() {
|
|
||||||
return lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setFailOnUnknown(boolean failOnUnknown) {
|
|
||||||
this.failOnUnknown = failOnUnknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean failOnUnknown() {
|
|
||||||
return failOnUnknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void beginArray() throws IOException {
|
@Override public void beginArray() throws IOException {
|
||||||
List<?> peeked = require(List.class, Token.BEGIN_ARRAY);
|
List<?> peeked = require(List.class, Token.BEGIN_ARRAY);
|
||||||
|
|
||||||
@@ -294,10 +272,6 @@ final class ObjectJsonReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String getPath() {
|
|
||||||
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override void promoteNameToValue() throws IOException {
|
@Override void promoteNameToValue() throws IOException {
|
||||||
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
|
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
|
||||||
|
|
||||||
@@ -344,16 +318,6 @@ final class ObjectJsonReader extends JsonReader {
|
|||||||
throw typeMismatch(name, Token.NAME);
|
throw typeMismatch(name, Token.NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonDataException typeMismatch(Object value, Object expected) {
|
|
||||||
if (value == null) {
|
|
||||||
return new JsonDataException(
|
|
||||||
"Expected " + expected + " but was null at path " + getPath());
|
|
||||||
} else {
|
|
||||||
return new JsonDataException("Expected " + expected + " but was " + value + ", a "
|
|
||||||
+ value.getClass().getName() + ", at path " + getPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a value and prepares for the next. If we're iterating a map or list this advances the
|
* Removes a value and prepares for the next. If we're iterating a map or list this advances the
|
||||||
* iterator.
|
* iterator.
|
||||||
|
@@ -27,19 +27,11 @@ import static com.squareup.moshi.JsonScope.NONEMPTY_DOCUMENT;
|
|||||||
|
|
||||||
/** Writes JSON by building a Java object comprising maps, lists, and JSON primitives. */
|
/** Writes JSON by building a Java object comprising maps, lists, and JSON primitives. */
|
||||||
final class ObjectJsonWriter extends JsonWriter {
|
final class ObjectJsonWriter extends JsonWriter {
|
||||||
private String indent;
|
|
||||||
private boolean lenient;
|
|
||||||
private boolean serializeNulls;
|
|
||||||
|
|
||||||
private final Object[] stack = new Object[32];
|
private final Object[] stack = new Object[32];
|
||||||
private final int[] scopes = new int[32];
|
|
||||||
private final String[] pathNames = new String[32];
|
|
||||||
private final int[] pathIndices = new int[32];
|
|
||||||
private int stackSize = 0;
|
|
||||||
private String deferredName;
|
private String deferredName;
|
||||||
|
|
||||||
ObjectJsonWriter() {
|
ObjectJsonWriter() {
|
||||||
scopes[stackSize++] = EMPTY_DOCUMENT;
|
pushScope(EMPTY_DOCUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object root() {
|
public Object root() {
|
||||||
@@ -50,30 +42,6 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
return stack[0];
|
return stack[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setIndent(String indent) {
|
|
||||||
this.indent = indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String getIndent() {
|
|
||||||
return indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isLenient() {
|
|
||||||
return lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setSerializeNulls(boolean serializeNulls) {
|
|
||||||
this.serializeNulls = serializeNulls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean getSerializeNulls() {
|
|
||||||
return serializeNulls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public JsonWriter beginArray() throws IOException {
|
@Override public JsonWriter beginArray() throws IOException {
|
||||||
if (stackSize == stack.length) {
|
if (stackSize == stack.length) {
|
||||||
throw new JsonDataException("Nesting too deep at " + getPath() + ": circular reference?");
|
throw new JsonDataException("Nesting too deep at " + getPath() + ": circular reference?");
|
||||||
@@ -81,14 +49,13 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
add(list);
|
add(list);
|
||||||
stack[stackSize] = list;
|
stack[stackSize] = list;
|
||||||
scopes[stackSize] = EMPTY_ARRAY;
|
|
||||||
pathIndices[stackSize] = 0;
|
pathIndices[stackSize] = 0;
|
||||||
stackSize++;
|
pushScope(EMPTY_ARRAY);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter endArray() throws IOException {
|
@Override public JsonWriter endArray() throws IOException {
|
||||||
if (peek() != EMPTY_ARRAY) {
|
if (peekScope() != EMPTY_ARRAY) {
|
||||||
throw new IllegalStateException("Nesting problem.");
|
throw new IllegalStateException("Nesting problem.");
|
||||||
}
|
}
|
||||||
stackSize--;
|
stackSize--;
|
||||||
@@ -104,13 +71,12 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
Map<String, Object> map = new LinkedHashTreeMap<>();
|
Map<String, Object> map = new LinkedHashTreeMap<>();
|
||||||
add(map);
|
add(map);
|
||||||
stack[stackSize] = map;
|
stack[stackSize] = map;
|
||||||
scopes[stackSize] = EMPTY_OBJECT;
|
pushScope(EMPTY_OBJECT);
|
||||||
stackSize++;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter endObject() throws IOException {
|
@Override public JsonWriter endObject() throws IOException {
|
||||||
if (peek() != EMPTY_OBJECT || deferredName != null) {
|
if (peekScope() != EMPTY_OBJECT || deferredName != null) {
|
||||||
throw new IllegalStateException("Nesting problem.");
|
throw new IllegalStateException("Nesting problem.");
|
||||||
}
|
}
|
||||||
stackSize--;
|
stackSize--;
|
||||||
@@ -127,7 +93,7 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
if (stackSize == 0) {
|
if (stackSize == 0) {
|
||||||
throw new IllegalStateException("JsonWriter is closed.");
|
throw new IllegalStateException("JsonWriter is closed.");
|
||||||
}
|
}
|
||||||
if (peek() != EMPTY_OBJECT || deferredName != null) {
|
if (peekScope() != EMPTY_OBJECT || deferredName != null) {
|
||||||
throw new IllegalStateException("Nesting problem.");
|
throw new IllegalStateException("Nesting problem.");
|
||||||
}
|
}
|
||||||
pathNames[stackSize - 1] = name;
|
pathNames[stackSize - 1] = name;
|
||||||
@@ -136,19 +102,27 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(String value) throws IOException {
|
@Override public JsonWriter value(String value) throws IOException {
|
||||||
return add(value);
|
add(value);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter nullValue() throws IOException {
|
@Override public JsonWriter nullValue() throws IOException {
|
||||||
return add(null);
|
add(null);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(boolean value) throws IOException {
|
@Override public JsonWriter value(boolean value) throws IOException {
|
||||||
return add(value);
|
add(value);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(Boolean value) throws IOException {
|
@Override public JsonWriter value(Boolean value) throws IOException {
|
||||||
return add(value);
|
add(value);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(double value) throws IOException {
|
@Override public JsonWriter value(double value) throws IOException {
|
||||||
@@ -156,7 +130,9 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(long value) throws IOException {
|
@Override public JsonWriter value(long value) throws IOException {
|
||||||
return add(value);
|
add(value);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public JsonWriter value(Number value) throws IOException {
|
@Override public JsonWriter value(Number value) throws IOException {
|
||||||
@@ -166,17 +142,15 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return add(value);
|
add(value);
|
||||||
|
pathIndices[stackSize - 1]++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override void promoteNameToValue() throws IOException {
|
@Override void promoteNameToValue() throws IOException {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String getPath() {
|
|
||||||
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void close() throws IOException {
|
@Override public void close() throws IOException {
|
||||||
int size = stackSize;
|
int size = stackSize;
|
||||||
if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) {
|
if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) {
|
||||||
@@ -191,18 +165,8 @@ final class ObjectJsonWriter extends JsonWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the scope on the top of the stack.
|
|
||||||
*/
|
|
||||||
private int peek() {
|
|
||||||
if (stackSize == 0) {
|
|
||||||
throw new IllegalStateException("JsonWriter is closed.");
|
|
||||||
}
|
|
||||||
return scopes[stackSize - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectJsonWriter add(Object newTop) {
|
private ObjectJsonWriter add(Object newTop) {
|
||||||
int scope = peek();
|
int scope = peekScope();
|
||||||
|
|
||||||
if (stackSize == 1) {
|
if (stackSize == 1) {
|
||||||
if (scope != EMPTY_DOCUMENT) {
|
if (scope != EMPTY_DOCUMENT) {
|
||||||
|
@@ -17,14 +17,27 @@ package com.squareup.moshi;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import okio.Buffer;
|
import java.util.List;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameter;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
public final class JsonWriterPathTest {
|
public final class JsonWriterPathTest {
|
||||||
|
@Parameter public JsonWriterFactory factory;
|
||||||
|
|
||||||
|
@Parameters(name = "{0}")
|
||||||
|
public static List<Object[]> parameters() {
|
||||||
|
return JsonWriterFactory.factories();
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void path() throws IOException {
|
@Test public void path() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
assertThat(writer.getPath()).isEqualTo("$.");
|
assertThat(writer.getPath()).isEqualTo("$.");
|
||||||
@@ -63,7 +76,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void arrayOfObjects() throws IOException {
|
@Test public void arrayOfObjects() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
assertThat(writer.getPath()).isEqualTo("$[0]");
|
assertThat(writer.getPath()).isEqualTo("$[0]");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
@@ -83,7 +96,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void arrayOfArrays() throws IOException {
|
@Test public void arrayOfArrays() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
assertThat(writer.getPath()).isEqualTo("$[0]");
|
assertThat(writer.getPath()).isEqualTo("$[0]");
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
@@ -103,7 +116,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void objectPath() throws IOException {
|
@Test public void objectPath() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
assertThat(writer.getPath()).isEqualTo("$.");
|
assertThat(writer.getPath()).isEqualTo("$.");
|
||||||
@@ -122,7 +135,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void nestedObjects() throws IOException {
|
@Test public void nestedObjects() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
assertThat(writer.getPath()).isEqualTo("$.");
|
assertThat(writer.getPath()).isEqualTo("$.");
|
||||||
@@ -147,7 +160,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void arrayPath() throws IOException {
|
@Test public void arrayPath() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
assertThat(writer.getPath()).isEqualTo("$[0]");
|
assertThat(writer.getPath()).isEqualTo("$[0]");
|
||||||
@@ -168,7 +181,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void nestedArrays() throws IOException {
|
@Test public void nestedArrays() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
assertThat(writer.getPath()).isEqualTo("$[0]");
|
assertThat(writer.getPath()).isEqualTo("$[0]");
|
||||||
@@ -189,7 +202,9 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
|
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
assumeTrue(factory.supportsMultipleTopLevelValuesInOneDocument());
|
||||||
|
|
||||||
|
JsonWriter writer = factory.newWriter();
|
||||||
writer.setLenient(true);
|
writer.setLenient(true);
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
writer.endArray();
|
writer.endArray();
|
||||||
@@ -200,7 +215,7 @@ public final class JsonWriterPathTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void skipNulls() throws IOException {
|
@Test public void skipNulls() throws IOException {
|
||||||
JsonWriter writer = JsonWriter.of(new Buffer());
|
JsonWriter writer = factory.newWriter();
|
||||||
writer.setSerializeNulls(false);
|
writer.setSerializeNulls(false);
|
||||||
assertThat(writer.getPath()).isEqualTo("$");
|
assertThat(writer.getPath()).isEqualTo("$");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
|
Reference in New Issue
Block a user