Update reader and writer to RFC 7159.

This commit is contained in:
Jake Wharton
2016-01-18 00:52:45 -05:00
parent ebe8af75ff
commit 97d2bf5e66
4 changed files with 73 additions and 73 deletions

View File

@@ -23,7 +23,7 @@ import okio.BufferedSource;
import okio.ByteString;
/**
* 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/rfc7159.txt">RFC 7159</a>)
* encoded value as a stream of tokens. This stream includes both literal
* values (strings, numbers, booleans, and nulls) as well as the begin and
* end delimiters of objects and arrays. The tokens are traversed in
@@ -272,7 +272,7 @@ public class JsonReader implements Closeable {
/**
* Configure this parser to be liberal in what it accepts. By default
* this parser is strict and only accepts JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
* href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the
* parser to lenient causes it to ignore the following syntax errors:
*
* <ul>
@@ -568,9 +568,6 @@ public class JsonReader implements Closeable {
buffer.readByte(); // Consume '\''.
return peeked = PEEKED_SINGLE_QUOTED;
case '"':
if (stackSize == 1) {
checkLenient();
}
buffer.readByte(); // Consume '\"'.
return peeked = PEEKED_DOUBLE_QUOTED;
case '[':
@@ -582,10 +579,6 @@ public class JsonReader implements Closeable {
default:
}
if (stackSize == 1) {
checkLenient(); // Top-level value isn't an array or an object.
}
int result = peekKeyword();
if (result != PEEKED_NONE) {
return result;

View File

@@ -30,7 +30,7 @@ import static com.squareup.moshi.JsonScope.NONEMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value to a stream, one token at a time. The stream includes both
* literal values (strings, numbers, booleans and nulls) as well as the begin
* and end delimiters of objects and arrays.
@@ -127,7 +127,7 @@ import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
public class JsonWriter implements Closeable, Flushable {
/*
* From RFC 4627, "All Unicode characters may be placed within the
* From RFC 7159, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
@@ -217,7 +217,7 @@ public class JsonWriter implements Closeable, Flushable {
/**
* Configure this writer to relax its syntax rules. By default, this writer
* only emits well-formed JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
* href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the writer
* to lenient permits the following:
* <ul>
* <li>Top-level values of any type. With strict writing, the top-level
@@ -299,7 +299,7 @@ public class JsonWriter implements Closeable, Flushable {
* bracket.
*/
private JsonWriter open(int empty, String openBracket) throws IOException {
beforeValue(true);
beforeValue();
pathIndices[stackSize] = 0;
push(empty);
sink.writeUtf8(openBracket);
@@ -400,7 +400,7 @@ public class JsonWriter implements Closeable, Flushable {
return name(value);
}
writeDeferredName();
beforeValue(false);
beforeValue();
string(value);
pathIndices[stackSize - 1]++;
return this;
@@ -420,7 +420,7 @@ public class JsonWriter implements Closeable, Flushable {
return this; // skip the name and the value
}
}
beforeValue(false);
beforeValue();
sink.writeUtf8("null");
pathIndices[stackSize - 1]++;
return this;
@@ -433,7 +433,7 @@ public class JsonWriter implements Closeable, Flushable {
*/
public JsonWriter value(boolean value) throws IOException {
writeDeferredName();
beforeValue(false);
beforeValue();
sink.writeUtf8(value ? "true" : "false");
pathIndices[stackSize - 1]++;
return this;
@@ -454,7 +454,7 @@ public class JsonWriter implements Closeable, Flushable {
return name(Double.toString(value));
}
writeDeferredName();
beforeValue(false);
beforeValue();
sink.writeUtf8(Double.toString(value));
pathIndices[stackSize - 1]++;
return this;
@@ -470,7 +470,7 @@ public class JsonWriter implements Closeable, Flushable {
return name(Long.toString(value));
}
writeDeferredName();
beforeValue(false);
beforeValue();
sink.writeUtf8(Long.toString(value));
pathIndices[stackSize - 1]++;
return this;
@@ -497,7 +497,7 @@ public class JsonWriter implements Closeable, Flushable {
return name(string);
}
writeDeferredName();
beforeValue(false);
beforeValue();
sink.writeUtf8(string);
pathIndices[stackSize - 1]++;
return this;
@@ -591,12 +591,9 @@ public class JsonWriter implements Closeable, Flushable {
* Inserts any necessary separators and whitespace before a literal value,
* inline array, or inline object. Also adjusts the stack to expect either a
* closing bracket or another element.
*
* @param root true if the value is a new array or object, the two values
* permitted as top-level elements.
*/
@SuppressWarnings("fallthrough")
private void beforeValue(boolean root) throws IOException {
private void beforeValue() throws IOException {
switch (peek()) {
case NONEMPTY_DOCUMENT:
if (!lenient) {
@@ -605,10 +602,6 @@ public class JsonWriter implements Closeable, Flushable {
}
// fall-through
case EMPTY_DOCUMENT: // first in document
if (!lenient && !root) {
throw new IllegalStateException(
"JSON must start with an array or an object.");
}
replaceTop(NONEMPTY_DOCUMENT);
break;

View File

@@ -264,14 +264,6 @@ public final class JsonReaderTest {
}
}
@Test public void noTopLevelObject() {
try {
newReader("true").nextBoolean();
fail();
} catch (IOException expected) {
}
}
@Test public void characterUnescaping() throws IOException {
String json = "[\"a\","
+ "\"a\\\"\","
@@ -1341,46 +1333,38 @@ public final class JsonReaderTest {
}
}
@Test public void strictTopLevelString() {
JsonReader reader = newReader("\"a\"");
try {
reader.nextString();
fail();
} catch (IOException expected) {
}
@Test public void topLevelValueTypes() throws IOException {
JsonReader reader1 = newReader("true");
assertThat(reader1.nextBoolean()).isTrue();
assertThat(reader1.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader2 = newReader("false");
assertThat(reader2.nextBoolean()).isFalse();
assertThat(reader2.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader3 = newReader("null");
assertThat(reader3.nextNull()).isNull();
assertThat(reader3.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader4 = newReader("123");
assertThat(reader4.nextInt()).isEqualTo(123);
assertThat(reader4.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader5 = newReader("123.4");
assertThat(reader5.nextDouble()).isEqualTo(123.4);
assertThat(reader5.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader6 = newReader("\"a\"");
assertThat(reader6.nextString()).isEqualTo("a");
assertThat(reader6.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void lenientTopLevelString() throws IOException {
JsonReader reader = newReader("\"a\"");
reader.setLenient(true);
assertThat(reader.nextString()).isEqualTo("a");
@Test public void topLevelValueTypeWithSkipValue() throws IOException {
JsonReader reader = newReader("true");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictTopLevelValueType() {
JsonReader reader = newReader("true");
try {
reader.nextBoolean();
fail();
} catch (IOException expected) {
}
}
@Test public void lenientTopLevelValueType() throws IOException {
JsonReader reader = newReader("true");
reader.setLenient(true);
assertThat(reader.nextBoolean()).isTrue();
}
@Test public void strictTopLevelValueTypeWithSkipValue() {
JsonReader reader = newReader("true");
try {
reader.skipValue();
fail();
} catch (IOException expected) {
}
}
@Test @Ignore public void bomIgnoredAsFirstCharacterOfDocument() throws IOException {
JsonReader reader = newReader("\ufeff[]");
reader.beginArray();

View File

@@ -48,11 +48,41 @@ public final class JsonWriterTest {
assertThat(buffer.readUtf8()).isEqualTo("{\"a\":null}");
}
@Test public void wrongTopLevelType() throws IOException {
@Test public void topLevelValueTypes() throws IOException {
Buffer buffer = new Buffer();
JsonWriter jsonWriter = JsonWriter.of(buffer);
JsonWriter writer1 = JsonWriter.of(buffer);
writer1.value(true);
writer1.close();
assertThat(buffer.readUtf8()).isEqualTo("true");
JsonWriter writer2 = JsonWriter.of(buffer);
writer2.nullValue();
writer2.close();
assertThat(buffer.readUtf8()).isEqualTo("null");
JsonWriter writer3 = JsonWriter.of(buffer);
writer3.value(123);
writer3.close();
assertThat(buffer.readUtf8()).isEqualTo("123");
JsonWriter writer4 = JsonWriter.of(buffer);
writer4.value(123.4);
writer4.close();
assertThat(buffer.readUtf8()).isEqualTo("123.4");
JsonWriter writer5 = JsonWriter.of(buffer);
writer5.value("a");
writer5.close();
assertThat(buffer.readUtf8()).isEqualTo("\"a\"");
}
@Test public void invalidTopLevelTypes() throws IOException {
Buffer buffer = new Buffer();
JsonWriter writer = JsonWriter.of(buffer);
writer.name("hello");
try {
jsonWriter.value("a");
writer.value("world");
fail();
} catch (IllegalStateException expected) {
}