diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index 991edab4..d504b744 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -140,6 +140,8 @@ public final class JsonWriter implements Closeable { */ private String separator = ":"; + private boolean lenient; + /** * Creates a new instance that writes a JSON-encoded stream to {@code out}. * For best performance, ensure {@link Writer} is buffered; wrapping in @@ -170,6 +172,22 @@ public final class JsonWriter implements Closeable { } } + /** + * Configure this writer to relax its syntax rules. By default, this writer + * only emits well-formed JSON as specified by RFC 4627. Setting the writer + * to lenient permits the following: + * + */ + public void setLenient(boolean lenient) { + this.lenient = lenient; + } + /** * Begins encoding a new array. Each call to this method must be paired with * a call to {@link #endArray}. @@ -344,7 +362,8 @@ public final class JsonWriter implements Closeable { } String string = value.toString(); - if (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN")) { + if (!lenient + && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { throw new IllegalArgumentException("Numeric values must be finite, but was " + value); } beforeValue(false); @@ -384,13 +403,40 @@ public final class JsonWriter implements Closeable { * quotation mark, reverse solidus, and the control characters * (U+0000 through U+001F)." */ - if (c == '"' || c == '\\') { + switch (c) { + case '"': + case '\\': out.write('\\'); out.write(c); - } else if (c <= 0x1F) { - out.write(String.format("\\u%04x", (int) c)); - } else { - out.write(c); + break; + + case '\t': + out.write("\\t"); + break; + + case '\b': + out.write("\\b"); + break; + + case '\n': + out.write("\\n"); + break; + + case '\r': + out.write("\\r"); + break; + + case '\f': + out.write("\\f"); + break; + + default: + if (c <= 0x1F) { + out.write(String.format("\\u%04x", (int) c)); + } else { + out.write(c); + } + break; } } out.write("\""); @@ -433,7 +479,7 @@ public final class JsonWriter implements Closeable { private void beforeValue(boolean root) throws IOException { switch (peek()) { case EMPTY_DOCUMENT: // first in document - if (!root) { + if (!lenient && !root) { throw new IllegalStateException( "JSON must start with an array or an object."); } diff --git a/gson/src/test/java/com/google/gson/JsonObjectTest.java b/gson/src/test/java/com/google/gson/JsonObjectTest.java index 6573df46..38cf8ec4 100644 --- a/gson/src/test/java/com/google/gson/JsonObjectTest.java +++ b/gson/src/test/java/com/google/gson/JsonObjectTest.java @@ -59,15 +59,8 @@ public class JsonObjectTest extends TestCase { fail("Should not allow null property names."); } catch (IllegalArgumentException expected) { } - try { - jsonObj.add("", JsonNull.createJsonNull()); - fail("Should not allow empty property names."); - } catch (IllegalArgumentException expected) { } - - try { - jsonObj.add(" \t", JsonNull.createJsonNull()); - fail("Should not allow whitespace only property names."); - } catch (IllegalArgumentException expected) { } + jsonObj.add("", JsonNull.createJsonNull()); + jsonObj.add(" \t", JsonNull.createJsonNull()); } public void testAddingBooleanProperties() throws Exception {