Permit multiple top-level values in JsonWriter in lenient mode. Also fix some cases where we don't throw the right thing on a closed JsonWriter.

I'd prefer to not support multiple top-level values, but we support it in JsonReader and it's easier to be consistent. Kevin Hayen's patch pointed me in the right direction here, but I needed to do more work to cover some of the edge cases.

Fixes issue 397.
This commit is contained in:
Jesse Wilson 2012-02-12 20:42:16 +00:00
parent 5c978948a0
commit 2c8bec27d4
2 changed files with 122 additions and 8 deletions

View File

@ -314,7 +314,11 @@ public class JsonWriter implements Closeable {
* Returns the value on the top of the stack.
*/
private JsonScope peek() {
return stack.get(stack.size() - 1);
int size = stack.size();
if (size == 0) {
throw new IllegalStateException("JsonWriter is closed.");
}
return stack.get(size - 1);
}
/**
@ -337,6 +341,9 @@ public class JsonWriter implements Closeable {
if (deferredName != null) {
throw new IllegalStateException();
}
if (stack.isEmpty()) {
throw new IllegalStateException("JsonWriter is closed.");
}
deferredName = name;
return this;
}
@ -453,6 +460,9 @@ public class JsonWriter implements Closeable {
* and flushes that writer.
*/
public void flush() throws IOException {
if (stack.isEmpty()) {
throw new IllegalStateException("JsonWriter is closed.");
}
out.flush();
}
@ -464,9 +474,11 @@ public class JsonWriter implements Closeable {
public void close() throws IOException {
out.close();
if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
int size = stack.size();
if (size > 1 || size == 1 && stack.get(size - 1) != JsonScope.NONEMPTY_DOCUMENT) {
throw new IOException("Incomplete document");
}
stack.clear();
}
private void string(String value) throws IOException {
@ -574,8 +586,15 @@ public class JsonWriter implements Closeable {
* @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 {
switch (peek()) {
case NONEMPTY_DOCUMENT:
if (!lenient) {
throw new IllegalStateException(
"JSON must have only one top-level value.");
}
// fall-through
case EMPTY_DOCUMENT: // first in document
if (!lenient && !root) {
throw new IllegalStateException(
@ -599,10 +618,6 @@ public class JsonWriter implements Closeable {
replaceTop(JsonScope.NONEMPTY_OBJECT);
break;
case NONEMPTY_DOCUMENT:
throw new IllegalStateException(
"JSON must have only one top-level value.");
default:
throw new IllegalStateException("Nesting problem: " + stack);
}

View File

@ -16,12 +16,11 @@
package com.google.gson.stream;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import junit.framework.TestCase;
public final class JsonWriterTest extends TestCase {
@ -464,4 +463,104 @@ public final class JsonWriterTest extends TestCase {
+ "]";
assertEquals(expected, stringWriter.toString());
}
public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.setLenient(true);
writer.beginArray();
writer.endArray();
writer.beginArray();
writer.endArray();
writer.close();
assertEquals("[][]", stringWriter.toString());
}
public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
try {
writer.beginArray();
fail();
} catch (IllegalStateException expected) {
}
}
public void testClosedWriterThrowsOnStructure() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
writer.close();
try {
writer.beginArray();
fail();
} catch (IllegalStateException expected) {
}
try {
writer.endArray();
fail();
} catch (IllegalStateException expected) {
}
try {
writer.beginObject();
fail();
} catch (IllegalStateException expected) {
}
try {
writer.endObject();
fail();
} catch (IllegalStateException expected) {
}
}
public void testClosedWriterThrowsOnName() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
writer.close();
try {
writer.name("a");
fail();
} catch (IllegalStateException expected) {
}
}
public void testClosedWriterThrowsOnValue() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
writer.close();
try {
writer.value("a");
fail();
} catch (IllegalStateException expected) {
}
}
public void testClosedWriterThrowsOnFlush() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
writer.close();
try {
writer.flush();
fail();
} catch (IllegalStateException expected) {
}
}
public void testWriterCloseIsIdempotent() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.endArray();
writer.close();
writer.close();
}
}