From a0cd8a4fc054ef3ffd881ccf5a22ec5bc06a448a Mon Sep 17 00:00:00 2001 From: Eric Cochran Date: Sun, 29 Apr 2018 23:49:47 -0700 Subject: [PATCH] Allow writing out raw JSON. --- .../com/squareup/moshi/JsonUtf8Writer.java | 13 +++++++++++++ .../com/squareup/moshi/JsonValueWriter.java | 18 ++++++++++++++++++ .../java/com/squareup/moshi/JsonWriter.java | 10 ++++++++++ .../squareup/moshi/JsonUtf8WriterTest.java | 16 ++++++++++++++++ .../squareup/moshi/JsonValueWriterTest.java | 19 ++++++++++++++++++- .../moshi/PromoteNameToValueTest.java | 18 ++++++++++++++++++ 6 files changed, 93 insertions(+), 1 deletion(-) diff --git a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java index 5cedfe1..7a7a727 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java @@ -18,6 +18,7 @@ package com.squareup.moshi; import java.io.IOException; import javax.annotation.Nullable; import okio.BufferedSink; +import okio.BufferedSource; import okio.Sink; import static com.squareup.moshi.JsonScope.DANGLING_NAME; @@ -261,6 +262,18 @@ final class JsonUtf8Writer extends JsonWriter { return this; } + @Override public JsonWriter value(BufferedSource source) throws IOException { + if (promoteValueToName) { + throw new IllegalStateException( + "BufferedSource cannot be used as a map key in JSON at path " + getPath()); + } + writeDeferredName(); + beforeValue(); + sink.writeAll(source); + pathIndices[stackSize - 1]++; + return this; + } + /** * Ensures all buffered data is written to the underlying {@link Sink} * and flushes that writer. diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java b/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java index 9bc06b3..64878ef 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nullable; +import okio.BufferedSource; import static com.squareup.moshi.JsonScope.EMPTY_ARRAY; import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT; @@ -205,6 +206,23 @@ final class JsonValueWriter extends JsonWriter { return this; } + @Override public JsonWriter value(BufferedSource source) throws IOException { + if (promoteValueToName) { + throw new IllegalStateException( + "BufferedSource cannot be used as a map key in JSON at path " + getPath()); + } + Object value = JsonReader.of(source).readJsonValue(); + boolean serializeNulls = this.serializeNulls; + this.serializeNulls = true; + try { + add(value); + } finally { + this.serializeNulls = serializeNulls; + } + pathIndices[stackSize - 1]++; + return this; + } + @Override public void close() throws IOException { int size = stackSize; if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) { diff --git a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java index 70e9513..678171c 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java @@ -22,6 +22,7 @@ import java.util.Arrays; import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; import okio.BufferedSink; +import okio.BufferedSource; import static com.squareup.moshi.JsonScope.EMPTY_OBJECT; import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT; @@ -335,6 +336,15 @@ public abstract class JsonWriter implements Closeable, Flushable { */ public abstract JsonWriter value(@Nullable Number value) throws IOException; + /** + * Writes {@code source} directly without encoding its contents. + * Since no validation is performed, {@link #setSerializeNulls} and other writer configurations + * are not respected. + * + * @return this writer. + */ + public abstract JsonWriter value(BufferedSource source) throws IOException; + /** * Changes the writer to treat the next value as a string name. This is useful for map adapters so * that arbitrary type adapters can use {@link #value} to write a name value. diff --git a/moshi/src/test/java/com/squareup/moshi/JsonUtf8WriterTest.java b/moshi/src/test/java/com/squareup/moshi/JsonUtf8WriterTest.java index b5b424a..0867921 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonUtf8WriterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonUtf8WriterTest.java @@ -107,4 +107,20 @@ public final class JsonUtf8WriterTest { // JsonWriter doesn't attempt to detect duplicate names assertThat(buffer.readUtf8()).isEqualTo("{\"a\":1,\"a\":2}"); } + + @Test public void valueFromSource() throws IOException { + Buffer buffer = new Buffer(); + JsonWriter writer = JsonUtf8Writer.of(buffer); + writer.beginObject(); + writer.name("a"); + writer.value(new Buffer().writeUtf8("[\"value\"]")); + writer.name("b"); + writer.value(new Buffer().writeUtf8("2")); + writer.name("c"); + writer.value(3); + writer.name("d"); + writer.value(new Buffer().writeUtf8("null")); + writer.endObject(); + assertThat(buffer.readUtf8()).isEqualTo("{\"a\":[\"value\"],\"b\":2,\"c\":3,\"d\":null}"); + } } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonValueWriterTest.java b/moshi/src/test/java/com/squareup/moshi/JsonValueWriterTest.java index 2087cac..a321104 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonValueWriterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonValueWriterTest.java @@ -23,8 +23,10 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import okio.Buffer; import org.junit.Test; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.junit.Assert.fail; @@ -239,6 +241,22 @@ public final class JsonValueWriterTest { assertThat((List) writer.root()).isEqualTo(numbers); } + @Test public void valueFromSource() throws IOException { + JsonValueWriter writer = new JsonValueWriter(); + writer.beginObject(); + writer.name("a"); + writer.value(new Buffer().writeUtf8("[\"value\"]")); + writer.name("b"); + writer.value(new Buffer().writeUtf8("2")); + writer.name("c"); + writer.value(3); + writer.name("d"); + writer.value(new Buffer().writeUtf8("null")); + writer.endObject(); + assertThat((Map) writer.root()).containsExactly( + entry("a", singletonList("value")), entry("b", 2.0d), entry("c", 3L), entry("d", null)); + } + /** * Returns an instance of number whose {@link #toString} is {@code s}. Using the standard number * methods like {@link Number#doubleValue} are awkward because they may truncate or discard @@ -268,4 +286,3 @@ public final class JsonValueWriterTest { }; } } - diff --git a/moshi/src/test/java/com/squareup/moshi/PromoteNameToValueTest.java b/moshi/src/test/java/com/squareup/moshi/PromoteNameToValueTest.java index 335e9d0..02064c4 100644 --- a/moshi/src/test/java/com/squareup/moshi/PromoteNameToValueTest.java +++ b/moshi/src/test/java/com/squareup/moshi/PromoteNameToValueTest.java @@ -16,6 +16,7 @@ package com.squareup.moshi; import java.util.List; +import okio.Buffer; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -333,4 +334,21 @@ public final class PromoteNameToValueTest { } writer.name("a"); } + + @Test public void writerSourceValueFails() throws Exception { + JsonWriter writer = factory.newWriter(); + writer.beginObject(); + writer.promoteValueToName(); + try { + writer.value(new Buffer().writeUtf8("\"a\"")); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected).hasMessage( + "BufferedSource cannot be used as a map key in JSON at path $."); + } + writer.value("a"); + writer.value("a value"); + writer.endObject(); + assertThat(factory.json()).isEqualTo("{\"a\":\"a value\"}"); + } }