diff --git a/moshi/src/main/java/com/squareup/moshi/JsonReader.java b/moshi/src/main/java/com/squareup/moshi/JsonReader.java index 00f37ce..af1f883 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonReader.java @@ -21,6 +21,7 @@ import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.CheckReturnValue; @@ -197,6 +198,8 @@ public abstract class JsonReader implements Closeable { /** True to throw a {@link JsonDataException} on any attempt to call {@link #skipValue()}. */ boolean failOnUnknown; + private Map, Object> tags; + /** Returns a new instance that reads UTF-8 encoded JSON from {@code source}. */ @CheckReturnValue public static JsonReader of(BufferedSource source) { @@ -569,6 +572,27 @@ public abstract class JsonReader implements Closeable { return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices); } + /** Returns the tag value for the given class key. */ + @SuppressWarnings("unchecked") + @CheckReturnValue + public final @Nullable T tag(Class clazz) { + if (tags == null) { + return null; + } + return (T) tags.get(clazz); + } + + /** Assigns the tag value using the given class key and value. */ + public final void setTag(Class clazz, T value) { + if (!clazz.isAssignableFrom(value.getClass())) { + throw new IllegalArgumentException("Tag value must be of type " + clazz.getName()); + } + if (tags == null) { + tags = new LinkedHashMap<>(); + } + tags.put(clazz, value); + } + /** * Changes the reader to treat the next name as a string value. This is useful for map adapters so * that arbitrary type adapters can use {@link #nextString} to read a name value. diff --git a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java index 7b45b40..024900e 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java @@ -24,6 +24,7 @@ import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.CheckReturnValue; @@ -171,6 +172,8 @@ public abstract class JsonWriter implements Closeable, Flushable { */ int flattenStackSize = -1; + private Map, Object> tags; + /** Returns a new instance that writes UTF-8 encoded JSON to {@code sink}. */ @CheckReturnValue public static JsonWriter of(BufferedSink sink) { @@ -561,4 +564,25 @@ public abstract class JsonWriter implements Closeable, Flushable { public final String getPath() { return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices); } + + /** Returns the tag value for the given class key. */ + @SuppressWarnings("unchecked") + @CheckReturnValue + public final @Nullable T tag(Class clazz) { + if (tags == null) { + return null; + } + return (T) tags.get(clazz); + } + + /** Assigns the tag value using the given class key and value. */ + public final void setTag(Class clazz, T value) { + if (!clazz.isAssignableFrom(value.getClass())) { + throw new IllegalArgumentException("Tag value must be of type " + clazz.getName()); + } + if (tags == null) { + tags = new LinkedHashMap<>(); + } + tags.put(clazz, value); + } } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java index 0da3667..b7cdbaf 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java @@ -1433,6 +1433,27 @@ public final class JsonReaderTest { assertThat(reader.nextString()).isEqualTo("d"); } + @SuppressWarnings("rawtypes") + @Test + public void tags() throws IOException { + JsonReader reader = newReader("{}"); + assertThat(reader.tag(Integer.class)).isNull(); + assertThat(reader.tag(CharSequence.class)).isNull(); + + reader.setTag(Integer.class, 1); + reader.setTag(CharSequence.class, "Foo"); + try { + reader.setTag((Class) CharSequence.class, 1); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessage("Tag value must be of type java.lang.CharSequence"); + } + + assertThat(reader.tag(Integer.class)).isEqualTo(1).isInstanceOf(Integer.class); + assertThat(reader.tag(CharSequence.class)).isEqualTo("Foo").isInstanceOf(String.class); + assertThat(reader.tag(String.class)).isNull(); + } + /** Peek a value, then read it, recursively. */ private void readValue(JsonReader reader, boolean peekJsonFirst) throws IOException { JsonReader.Token token = reader.peek(); diff --git a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java index 76b1c59..19e55fb 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java @@ -953,4 +953,25 @@ public final class JsonWriterTest { writer.endObject(); assertThat(factory.json()).isEqualTo("{}"); } + + @SuppressWarnings("rawtypes") + @Test + public void tags() throws IOException { + JsonWriter writer = factory.newWriter(); + assertThat(writer.tag(Integer.class)).isNull(); + assertThat(writer.tag(CharSequence.class)).isNull(); + + writer.setTag(Integer.class, 1); + writer.setTag(CharSequence.class, "Foo"); + try { + writer.setTag((Class) CharSequence.class, 1); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessage("Tag value must be of type java.lang.CharSequence"); + } + + assertThat(writer.tag(Integer.class)).isEqualTo(1).isInstanceOf(Integer.class); + assertThat(writer.tag(CharSequence.class)).isEqualTo("Foo").isInstanceOf(String.class); + assertThat(writer.tag(String.class)).isNull(); + } }