Add JsonReader.skipName.

This commit is contained in:
Eric Cochran
2018-04-30 18:41:18 -07:00
parent 7018cec47d
commit b848f1cc52
9 changed files with 82 additions and 7 deletions

View File

@@ -151,7 +151,7 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
while (reader.hasNext()) {
int index = reader.selectName(options);
if (index == -1) {
reader.nextName();
reader.skipName();
reader.skipValue();
continue;
}

View File

@@ -219,9 +219,9 @@ public abstract class JsonAdapter<T> {
/**
* 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<T> failOnUnknown() {
final JsonAdapter<T> delegate = this;

View File

@@ -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.
*
* <p>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.

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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");
}
}