From b848f1cc52bbfa13b626057d911d53e1a79e3d2e Mon Sep 17 00:00:00 2001 From: Eric Cochran Date: Mon, 30 Apr 2018 18:41:18 -0700 Subject: [PATCH] Add JsonReader.skipName. --- .../moshi/kotlin/KotlinJsonAdapter.kt | 2 +- .../com/squareup/moshi/ClassJsonAdapter.java | 2 +- .../java/com/squareup/moshi/JsonAdapter.java | 6 +++--- .../java/com/squareup/moshi/JsonReader.java | 11 +++++++++- .../com/squareup/moshi/JsonUtf8Reader.java | 21 +++++++++++++++++++ .../com/squareup/moshi/JsonValueReader.java | 12 +++++++++++ .../com/squareup/moshi/JsonReaderTest.java | 19 +++++++++++++++++ .../squareup/moshi/JsonUtf8ReaderTest.java | 14 +++++++++++++ .../java/com/squareup/moshi/MoshiTest.java | 2 +- 9 files changed, 82 insertions(+), 7 deletions(-) diff --git a/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt index 922cc11..3d9c109 100644 --- a/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt +++ b/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt @@ -68,7 +68,7 @@ internal class KotlinJsonAdapter( val binding = if (index != -1) bindings[index] else null if (binding == null) { - reader.nextName() + reader.skipName() reader.skipValue() continue } diff --git a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java index 3166349..5517de5 100644 --- a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java @@ -151,7 +151,7 @@ final class ClassJsonAdapter extends JsonAdapter { while (reader.hasNext()) { int index = reader.selectName(options); if (index == -1) { - reader.nextName(); + reader.skipName(); reader.skipValue(); continue; } diff --git a/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java index 345c027..7c46133 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java @@ -219,9 +219,9 @@ public abstract class JsonAdapter { /** * Returns a JSON adapter equal to this, but that throws a {@link JsonDataException} when - * {@linkplain JsonReader#setFailOnUnknown(boolean) unknown values} are encountered. This - * constraint applies to both the top-level message handled by this type adapter as well as to - * nested messages. + * {@linkplain JsonReader#setFailOnUnknown(boolean) unknown names and values} are encountered. + * This constraint applies to both the top-level message handled by this type adapter as well as + * to nested messages. */ @CheckReturnValue public final JsonAdapter failOnUnknown() { final JsonAdapter delegate = this; diff --git a/moshi/src/main/java/com/squareup/moshi/JsonReader.java b/moshi/src/main/java/com/squareup/moshi/JsonReader.java index 1ecd878..29863bb 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonReader.java @@ -279,7 +279,7 @@ public abstract class JsonReader implements Closeable { } /** - * Returns true if this parser forbids skipping values. + * Returns true if this parser forbids skipping names and values. */ @CheckReturnValue public final boolean failOnUnknown() { return failOnUnknown; @@ -332,6 +332,15 @@ public abstract class JsonReader implements Closeable { */ public abstract int selectName(Options options) throws IOException; + /** + * Skips the next token, consuming it. This method is intended for use when the JSON token stream + * contains unrecognized or unhandled names. + * + *

This throws a {@link JsonDataException} if this parser has been configured to {@linkplain + * #failOnUnknown fail on unknown} names. + */ + public abstract void skipName() throws IOException; + /** * Returns the {@linkplain Token#STRING string} value of the next token, consuming it. If the next * token is a number, this method will return its string form. diff --git a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java index 6ea058b..200ab07 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java @@ -562,6 +562,27 @@ final class JsonUtf8Reader extends JsonReader { return result; } + @Override public void skipName() throws IOException { + if (failOnUnknown) { + throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); + } + int p = peeked; + if (p == PEEKED_NONE) { + p = doPeek(); + } + if (p == PEEKED_UNQUOTED_NAME) { + skipUnquotedValue(); + } else if (p == PEEKED_DOUBLE_QUOTED_NAME) { + skipQuotedValue(DOUBLE_QUOTE_OR_SLASH); + } else if (p == PEEKED_SINGLE_QUOTED_NAME) { + skipQuotedValue(SINGLE_QUOTE_OR_SLASH); + } else if (p != PEEKED_BUFFERED_NAME) { + throw new JsonDataException("Expected a name but was " + peek() + " at path " + getPath()); + } + peeked = PEEKED_NONE; + pathNames[stackSize - 1] = "null"; + } + /** * If {@code name} is in {@code options} this consumes it and returns its index. * Otherwise this returns -1 and no name is consumed. diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java index 063c35c..5babf47 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java @@ -151,6 +151,18 @@ final class JsonValueReader extends JsonReader { return -1; } + @Override public void skipName() throws IOException { + if (failOnUnknown) { + throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); + } + + Map.Entry peeked = require(Map.Entry.class, Token.NAME); + + // Swap the Map.Entry for its value on the stack. + stack[stackSize - 1] = peeked.getValue(); + pathNames[stackSize - 2] = "null"; + } + @Override public String nextString() throws IOException { Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null); if (peeked instanceof String) { diff --git a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java index d656998..86d7106 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java @@ -960,4 +960,23 @@ public final class JsonReaderTest { assertThat(value).isEqualTo( Collections.singletonMap("pizzas", Arrays.asList("cheese", "pepperoni"))); } + + @Test public void skipName() throws IOException { + JsonReader reader = newReader("{\"a\":1}"); + reader.beginObject(); + reader.skipName(); + assertThat(reader.peek()).isEqualTo(JsonReader.Token.NUMBER); + reader.skipValue(); + reader.endObject(); + } + + @Test public void skipNameOnValueFails() throws IOException { + JsonReader reader = newReader("1"); + try { + reader.skipName(); + fail(); + } catch (JsonDataException expected) { + } + assertThat(reader.nextInt()).isEqualTo(1); + } } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java index 89f28a0..7f1a504 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java @@ -994,6 +994,20 @@ public final class JsonUtf8ReaderTest { } } + @Test public void failureMessagePathFromSkipName() throws IOException { + JsonReader reader = newReader("{\"a\":[42,}"); + reader.beginObject(); + reader.skipName(); + reader.beginArray(); + reader.nextInt(); + try { + reader.peek(); + fail(); + } catch (JsonEncodingException expected) { + assertThat(expected).hasMessage("Expected value at path $.null[1]"); + } + } + @Test @Ignore public void strictVeryLongNumber() throws IOException { JsonReader reader = newReader("[0." + repeat('9', 8192) + "]"); reader.beginArray(); diff --git a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java index f81d272..c284ec0 100644 --- a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java +++ b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java @@ -910,7 +910,7 @@ public final class MoshiTest { adapter.fromJson("{\"diameter\":5,\"crust\":\"thick\",\"extraCheese\":true}"); fail(); } catch (JsonDataException expected) { - assertThat(expected).hasMessage("Cannot skip unexpected STRING at $.crust"); + assertThat(expected).hasMessage("Cannot skip unexpected NAME at $.diameter"); } }