Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve JsonReader.skipValue() #2062

Expand Up @@ -96,6 +96,7 @@ public JsonTreeReader(JsonElement element) {
popStack(); // empty iterator
popStack(); // object
if (stackSize > 0) {
pathNames[stackSize - 1] = null; // Free the last path name so that it can be garbage collected
pathIndices[stackSize - 1]++;
}
}
Expand Down Expand Up @@ -269,14 +270,18 @@ JsonElement nextJsonElement() throws IOException {
}

@Override public void skipValue() throws IOException {
if (peek() == JsonToken.NAME) {
JsonToken peeked = peek();
if (peeked == JsonToken.NAME) {
Marcono1234 marked this conversation as resolved.
Show resolved Hide resolved
nextName();
pathNames[stackSize - 2] = "null";
pathNames[stackSize - 2] = "<skipped>";
} else if (peeked == JsonToken.END_ARRAY) {
endArray();
} else if (peeked == JsonToken.END_OBJECT) {
endObject();
} else if (peeked == JsonToken.END_DOCUMENT) {
Marcono1234 marked this conversation as resolved.
Show resolved Hide resolved
throw new IllegalStateException("Cannot skip at end of document");
} else {
popStack();
if (stackSize > 0) {
pathNames[stackSize - 1] = "null";
}
}
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
Expand Down
46 changes: 38 additions & 8 deletions gson/src/main/java/com/google/gson/stream/JsonReader.java
Expand Up @@ -1233,9 +1233,16 @@ public int nextInt() throws IOException {
}

/**
* Skips the next value recursively. If it is an object or array, all nested
* elements are skipped. This method is intended for use when the JSON token
* stream contains unrecognized or unhandled values.
* Skips the next value recursively. This method is intended for use when
* the JSON token stream contains unrecognized or unhandled values.
*
* <p>If the next value is a JSON object or array, all nested elements are
* skipped. For JSON objects when called before the name of a property has been
* consumed, only the name is skipped. When called at the end of a JSON array
* or object, the end of the array or object is skipped.
*
* @throws IllegalStateException when called at the end of the document, that is,
* after the last value.
*/
public void skipValue() throws IOException {
int count = 0;
Expand All @@ -1255,22 +1262,45 @@ public void skipValue() throws IOException {
stackSize--;
count--;
} else if (p == PEEKED_END_OBJECT) {
// Only update when object end is explicitly skipped, otherwise stack is not updated anyways
if (count == 0) {
pathNames[stackSize - 1] = null; // Free the last path name so that it can be garbage collected
}
stackSize--;
count--;
} else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
} else if (p == PEEKED_UNQUOTED) {
Marcono1234 marked this conversation as resolved.
Show resolved Hide resolved
skipUnquotedValue();
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
} else if (p == PEEKED_SINGLE_QUOTED) {
skipQuotedValue('\'');
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
} else if (p == PEEKED_DOUBLE_QUOTED) {
skipQuotedValue('"');
} else if (p == PEEKED_UNQUOTED_NAME) {
skipUnquotedValue();
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
if (count == 0) {
pathNames[stackSize - 1] = "<skipped>";
}
} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
skipQuotedValue('\'');
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
if (count == 0) {
pathNames[stackSize - 1] = "<skipped>";
}
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
skipQuotedValue('"');
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
if (count == 0) {
pathNames[stackSize - 1] = "<skipped>";
}
} else if (p == PEEKED_NUMBER) {
pos += peekedNumberLength;
} else if (p == PEEKED_EOF) {
throw new IllegalStateException("Cannot skip at end of document");
Marcono1234 marked this conversation as resolved.
Show resolved Hide resolved
}
peeked = PEEKED_NONE;
} while (count != 0);
} while (count > 0);

pathIndices[stackSize - 1]++;
pathNames[stackSize - 1] = "null";
}

private void push(int newTop) {
Expand Down
Expand Up @@ -55,6 +55,34 @@ public void testSkipValue_filledJsonObject() throws IOException {
assertEquals(JsonToken.END_DOCUMENT, in.peek());
}

public void testSkipValue_afterEndOfDocument() throws IOException {
JsonTreeReader reader = new JsonTreeReader(new JsonObject());
reader.beginObject();
reader.endObject();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());

try {
reader.skipValue();
fail();
} catch (IllegalStateException e) {
assertEquals("Cannot skip at end of document", e.getMessage());
}
}

public void testSkipValue_atArrayEnd() throws IOException {
JsonTreeReader reader = new JsonTreeReader(new JsonArray());
reader.beginArray();
reader.skipValue();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}

public void testSkipValue_atObjectEnd() throws IOException {
JsonTreeReader reader = new JsonTreeReader(new JsonObject());
reader.beginObject();
reader.skipValue();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}

public void testHasNext_endOfDocument() throws IOException {
JsonTreeReader reader = new JsonTreeReader(new JsonObject());
reader.beginObject();
Expand Down
Expand Up @@ -225,8 +225,8 @@ public static List<Object[]> parameters() {
JsonReader reader = factory.create("{\"a\":1}");
reader.beginObject();
reader.skipValue();
assertEquals("$.null", reader.getPreviousPath());
assertEquals("$.null", reader.getPath());
assertEquals("$.<skipped>", reader.getPreviousPath());
assertEquals("$.<skipped>", reader.getPath());
}

@Test public void skipObjectValues() throws IOException {
Expand All @@ -236,8 +236,8 @@ public static List<Object[]> parameters() {
assertEquals("$.", reader.getPath());
reader.nextName();
reader.skipValue();
assertEquals("$.null", reader.getPreviousPath());
assertEquals("$.null", reader.getPath());
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.nextName();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
Expand Down
28 changes: 28 additions & 0 deletions gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
Expand Up @@ -164,6 +164,34 @@ public void testSkipDouble() throws IOException {
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}

public void testSkipValueAfterEndOfDocument() throws IOException {
JsonReader reader = new JsonReader(reader("{}"));
reader.beginObject();
reader.endObject();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());

try {
reader.skipValue();
fail();
} catch (IllegalStateException e) {
assertEquals("Cannot skip at end of document", e.getMessage());
}
}

public void testSkipValueAtArrayEnd() throws IOException {
JsonReader reader = new JsonReader(reader("[]"));
reader.beginArray();
reader.skipValue();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}

public void testSkipValueAtObjectEnd() throws IOException {
JsonReader reader = new JsonReader(reader("{}"));
reader.beginObject();
reader.skipValue();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}

public void testHelloWorld() throws IOException {
String json = "{\n" +
" \"hello\": true,\n" +
Expand Down