Merge pull request #503 from square/eric.write-from-source

Allow writing out raw JSON.
This commit is contained in:
Jesse Wilson
2018-05-06 21:11:34 -04:00
committed by GitHub
6 changed files with 93 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ package com.squareup.moshi;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource;
import okio.Sink; import okio.Sink;
import static com.squareup.moshi.JsonScope.DANGLING_NAME; import static com.squareup.moshi.JsonScope.DANGLING_NAME;
@@ -261,6 +262,18 @@ final class JsonUtf8Writer extends JsonWriter {
return this; 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} * Ensures all buffered data is written to the underlying {@link Sink}
* and flushes that writer. * and flushes that writer.

View File

@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import okio.BufferedSource;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY; import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT; import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
@@ -205,6 +206,23 @@ final class JsonValueWriter extends JsonWriter {
return this; 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 { @Override public void close() throws IOException {
int size = stackSize; int size = stackSize;
if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) { if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) {

View File

@@ -22,6 +22,7 @@ import java.util.Arrays;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT; import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.NONEMPTY_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; 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 * 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. * that arbitrary type adapters can use {@link #value} to write a name value.

View File

@@ -107,4 +107,20 @@ public final class JsonUtf8WriterTest {
// JsonWriter doesn't attempt to detect duplicate names // JsonWriter doesn't attempt to detect duplicate names
assertThat(buffer.readUtf8()).isEqualTo("{\"a\":1,\"a\":2}"); 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}");
}
} }

View File

@@ -23,8 +23,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import okio.Buffer;
import org.junit.Test; 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.assertThat;
import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.entry;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@@ -239,6 +241,22 @@ public final class JsonValueWriterTest {
assertThat((List<?>) writer.root()).isEqualTo(numbers); 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 * 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 * methods like {@link Number#doubleValue} are awkward because they may truncate or discard
@@ -268,4 +286,3 @@ public final class JsonValueWriterTest {
}; };
} }
} }

View File

@@ -16,6 +16,7 @@
package com.squareup.moshi; package com.squareup.moshi;
import java.util.List; import java.util.List;
import okio.Buffer;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
@@ -333,4 +334,21 @@ public final class PromoteNameToValueTest {
} }
writer.name("a"); 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\"}");
}
} }