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:
+ *
+ * - Top-level values of any type. With strict writing, the top-level
+ * value must be an object or an array.
+ *
- Numbers may be {@link Double#isNaN() NaNs} or {@link
+ * Double#isInfinite() infinities}.
+ *
+ */
+ 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 {