Make default adapters stricter; improve exception messages (#2000)

* Make default adapters stricter; improve exception messages

* Reduce scope of synchronized blocks

* Improve JsonReader.getPath / getPreviousPath Javadoc
This commit is contained in:
Marcono1234 2021-11-01 23:08:04 +01:00 committed by GitHub
parent b3188c1132
commit b4dab86b10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 327 additions and 77 deletions

View File

@ -71,11 +71,11 @@ public enum ToNumberPolicy implements ToNumberStrategy {
try {
Double d = Double.valueOf(value);
if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) {
throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPath());
throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath());
}
return d;
} catch (NumberFormatException doubleE) {
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPath(), doubleE);
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE);
}
}
}
@ -91,7 +91,7 @@ public enum ToNumberPolicy implements ToNumberStrategy {
try {
return new BigDecimal(value);
} catch (NumberFormatException e) {
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPath(), e);
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e);
}
}
}

View File

@ -72,30 +72,36 @@ public final class DateTypeAdapter extends TypeAdapter<Date> {
in.nextNull();
return null;
}
return deserializeToDate(in.nextString());
return deserializeToDate(in);
}
private synchronized Date deserializeToDate(String json) {
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {
return dateFormat.parse(json);
return dateFormat.parse(s);
} catch (ParseException ignored) {}
}
}
try {
return ISO8601Utils.parse(json, new ParsePosition(0));
return ISO8601Utils.parse(s, new ParsePosition(0));
} catch (ParseException e) {
throw new JsonSyntaxException(json, e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
}
}
@Override public synchronized void write(JsonWriter out, Date value) throws IOException {
@Override public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String dateFormatAsString = dateFormats.get(0).format(value);
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
out.value(dateFormatAsString);
}
}

View File

@ -130,10 +130,13 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
out.nullValue();
return;
}
synchronized(dateFormats) {
String dateFormatAsString = dateFormats.get(0).format(value);
out.value(dateFormatAsString);
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
out.value(dateFormatAsString);
}
@Override
@ -142,11 +145,12 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
in.nextNull();
return null;
}
Date date = deserializeToDate(in.nextString());
Date date = deserializeToDate(in);
return dateType.deserialize(date);
}
private Date deserializeToDate(String s) {
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {
@ -158,7 +162,7 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
try {
return ISO8601Utils.parse(s, new ParsePosition(0));
} catch (ParseException e) {
throw new JsonSyntaxException(s, e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
}
}

View File

@ -304,12 +304,19 @@ public final class JsonTreeReader extends JsonReader {
stack[stackSize++] = newTop;
}
@Override public String getPath() {
private String getPath(boolean usePreviousPath) {
StringBuilder result = new StringBuilder().append('$');
for (int i = 0; i < stackSize; i++) {
if (stack[i] instanceof JsonArray) {
if (++i < stackSize && stack[i] instanceof Iterator) {
result.append('[').append(pathIndices[i]).append(']');
int pathIndex = pathIndices[i];
// If index is last path element it points to next array element; have to decrement
// `- 1` covers case where iterator for next element is on stack
// `- 2` covers case where peek() already pushed next element onto stack
if (usePreviousPath && pathIndex > 0 && (i == stackSize - 1 || i == stackSize - 2)) {
pathIndex--;
}
result.append('[').append(pathIndex).append(']');
}
} else if (stack[i] instanceof JsonObject) {
if (++i < stackSize && stack[i] instanceof Iterator) {
@ -323,6 +330,14 @@ public final class JsonTreeReader extends JsonReader {
return result.toString();
}
@Override public String getPreviousPath() {
return getPath(true);
}
@Override public String getPath() {
return getPath(false);
}
private String locationString() {
return " at path " + getPath();
}

View File

@ -72,7 +72,7 @@ public final class NumberTypeAdapter extends TypeAdapter<Number> {
case STRING:
return toNumberStrategy.readNumber(in);
default:
throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
throw new JsonSyntaxException("Expecting number, got: " + jsonToken + "; at path " + in.getPath());
}
}

View File

@ -92,22 +92,21 @@ public final class TypeAdapters {
boolean set;
switch (tokenType) {
case NUMBER:
set = in.nextInt() != 0;
case STRING:
int intValue = in.nextInt();
if (intValue == 0) {
set = false;
} else if (intValue == 1) {
set = true;
} else {
throw new JsonSyntaxException("Invalid bitset value " + intValue + ", expected 0 or 1; at path " + in.getPreviousPath());
}
break;
case BOOLEAN:
set = in.nextBoolean();
break;
case STRING:
String stringValue = in.nextString();
try {
set = Integer.parseInt(stringValue) != 0;
} catch (NumberFormatException e) {
throw new JsonSyntaxException(
"Error: Expecting: bitset number value (1, 0), Found: " + stringValue);
}
break;
default:
throw new JsonSyntaxException("Invalid bitset value type: " + tokenType);
throw new JsonSyntaxException("Invalid bitset value type: " + tokenType + "; at path " + in.getPath());
}
if (set) {
bitset.set(i);
@ -178,12 +177,18 @@ public final class TypeAdapters {
in.nextNull();
return null;
}
int intValue;
try {
int intValue = in.nextInt();
return (byte) intValue;
intValue = in.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
// Allow up to 255 to support unsigned values
if (intValue > 255 || intValue < Byte.MIN_VALUE) {
throw new JsonSyntaxException("Lossy conversion from " + intValue + " to byte; at path " + in.getPreviousPath());
}
return (byte) intValue;
}
@Override
public void write(JsonWriter out, Number value) throws IOException {
@ -201,11 +206,18 @@ public final class TypeAdapters {
in.nextNull();
return null;
}
int intValue;
try {
return (short) in.nextInt();
intValue = in.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
// Allow up to 65535 to support unsigned values
if (intValue > 65535 || intValue < Short.MIN_VALUE) {
throw new JsonSyntaxException("Lossy conversion from " + intValue + " to short; at path " + in.getPreviousPath());
}
return (short) intValue;
}
@Override
public void write(JsonWriter out, Number value) throws IOException {
@ -352,7 +364,7 @@ public final class TypeAdapters {
}
String str = in.nextString();
if (str.length() != 1) {
throw new JsonSyntaxException("Expecting character, got: " + str);
throw new JsonSyntaxException("Expecting character, got: " + str + "; at " + in.getPreviousPath());
}
return str.charAt(0);
}
@ -391,10 +403,11 @@ public final class TypeAdapters {
in.nextNull();
return null;
}
String s = in.nextString();
try {
return new BigDecimal(in.nextString());
return new BigDecimal(s);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e);
}
}
@ -409,10 +422,11 @@ public final class TypeAdapters {
in.nextNull();
return null;
}
String s = in.nextString();
try {
return new BigInteger(in.nextString());
return new BigInteger(s);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e);
}
}
@ -525,7 +539,12 @@ public final class TypeAdapters {
in.nextNull();
return null;
}
return java.util.UUID.fromString(in.nextString());
String s = in.nextString();
try {
return java.util.UUID.fromString(s);
} catch (IllegalArgumentException e) {
throw new JsonSyntaxException("Failed parsing '" + s + "' as UUID; at path " + in.getPreviousPath(), e);
}
}
@Override
public void write(JsonWriter out, UUID value) throws IOException {
@ -538,7 +557,12 @@ public final class TypeAdapters {
public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() {
@Override
public Currency read(JsonReader in) throws IOException {
return Currency.getInstance(in.nextString());
String s = in.nextString();
try {
return Currency.getInstance(s);
} catch (IllegalArgumentException e) {
throw new JsonSyntaxException("Failed parsing '" + s + "' as Currency; at path " + in.getPreviousPath(), e);
}
}
@Override
public void write(JsonWriter out, Currency value) throws IOException {
@ -866,7 +890,7 @@ public final class TypeAdapters {
T1 result = typeAdapter.read(in);
if (result != null && !requestedType.isInstance(result)) {
throw new JsonSyntaxException("Expected a " + requestedType.getName()
+ " but was " + result.getClass().getName());
+ " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath());
}
return result;
}

View File

@ -28,6 +28,7 @@ import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Adapter for java.sql.Date. Although this class appears stateless, it is not.
@ -50,21 +51,33 @@ final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
}
@Override
public synchronized java.sql.Date read(JsonReader in) throws IOException {
public java.sql.Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String s = in.nextString();
try {
final long utilDate = format.parse(in.nextString()).getTime();
return new java.sql.Date(utilDate);
Date utilDate;
synchronized (this) {
utilDate = format.parse(s);
}
return new java.sql.Date(utilDate.getTime());
} catch (ParseException e) {
throw new JsonSyntaxException(e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e);
}
}
@Override
public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException {
out.value(value == null ? null : format.format(value));
public void write(JsonWriter out, java.sql.Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String dateString;
synchronized (this) {
dateString = format.format(value);
}
out.value(dateString);
}
}

View File

@ -50,20 +50,31 @@ final class SqlTimeTypeAdapter extends TypeAdapter<Time> {
private SqlTimeTypeAdapter() {
}
@Override public synchronized Time read(JsonReader in) throws IOException {
@Override public Time read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String s = in.nextString();
try {
Date date = format.parse(in.nextString());
synchronized (this) {
Date date = format.parse(s);
return new Time(date.getTime());
}
} catch (ParseException e) {
throw new JsonSyntaxException(e);
throw new JsonSyntaxException("Failed parsing '" + s + "' as SQL Time; at path " + in.getPreviousPath(), e);
}
}
@Override public synchronized void write(JsonWriter out, Time value) throws IOException {
out.value(value == null ? null : format.format(value));
@Override public void write(JsonWriter out, Time value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String timeString;
synchronized (this) {
timeString = format.format(value);
}
out.value(timeString);
}
}

View File

@ -1454,19 +1454,19 @@ public class JsonReader implements Closeable {
return " at line " + line + " column " + column + " path " + getPath();
}
/**
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
* the current location in the JSON value.
*/
public String getPath() {
private String getPath(boolean usePreviousPath) {
StringBuilder result = new StringBuilder().append('$');
for (int i = 0, size = stackSize; i < size; i++) {
for (int i = 0; i < stackSize; i++) {
switch (stack[i]) {
case JsonScope.EMPTY_ARRAY:
case JsonScope.NONEMPTY_ARRAY:
result.append('[').append(pathIndices[i]).append(']');
int pathIndex = pathIndices[i];
// If index is last path element it points to next array element; have to decrement
if (usePreviousPath && pathIndex > 0 && i == stackSize - 1) {
pathIndex--;
}
result.append('[').append(pathIndex).append(']');
break;
case JsonScope.EMPTY_OBJECT:
case JsonScope.DANGLING_NAME:
case JsonScope.NONEMPTY_OBJECT:
@ -1475,7 +1475,6 @@ public class JsonReader implements Closeable {
result.append(pathNames[i]);
}
break;
case JsonScope.NONEMPTY_DOCUMENT:
case JsonScope.EMPTY_DOCUMENT:
case JsonScope.CLOSED:
@ -1485,6 +1484,41 @@ public class JsonReader implements Closeable {
return result.toString();
}
/**
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
* in <i>dot-notation</i> to the previous (or current) location in the JSON document:
* <ul>
* <li>For JSON arrays the path points to the index of the previous element.<br>
* If no element has been consumed yet it uses the index 0 (even if there are no elements).</li>
* <li>For JSON objects the path points to the last property, or to the current
* property if its value has not been consumed yet.</li>
* </ul>
*
* <p>This method can be useful to add additional context to exception messages
* <i>after</i> a value has been consumed.
*/
public String getPreviousPath() {
return getPath(true);
}
/**
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
* in <i>dot-notation</i> to the next (or current) location in the JSON document:
* <ul>
* <li>For JSON arrays the path points to the index of the next element (even
* if there are no further elements).</li>
* <li>For JSON objects the path points to the last property, or to the current
* property if its value has not been consumed yet.</li>
* </ul>
*
* <p>This method can be useful to add additional context to exception messages
* <i>before</i> a value is consumed, for example when the {@linkplain #peek() peeked}
* token is unexpected.
*/
public String getPath() {
return getPath(false);
}
/**
* Unescapes the character identified by the character or characters that
* immediately follow a backslash. The backslash '\' should have already

View File

@ -332,6 +332,20 @@ public class DefaultTypeAdaptersTest extends TestCase {
json = "[true,false,true,true,true,true,false,false,true,false,false]";
assertEquals(expected, gson.fromJson(json, BitSet.class));
try {
gson.fromJson("[1, []]", BitSet.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Invalid bitset value type: BEGIN_ARRAY; at path $[1]", e.getMessage());
}
try {
gson.fromJson("[1, 2]", BitSet.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Invalid bitset value 2, expected 0 or 1; at path $[1]", e.getMessage());
}
}
public void testDefaultDateSerialization() {
@ -567,7 +581,7 @@ public class DefaultTypeAdaptersTest extends TestCase {
gson.fromJson("\"abc\"", JsonObject.class);
fail();
} catch (JsonSyntaxException expected) {
assertEquals("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive",
assertEquals("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $",
expected.getMessage());
}
}

View File

@ -16,6 +16,8 @@
package com.google.gson.functional;
import static org.junit.Assert.assertArrayEquals;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonPrimitive;
@ -63,16 +65,75 @@ public class PrimitiveTest extends TestCase {
assertEquals("1", gson.toJson(1, Byte.class));
}
public void testByteDeserialization() {
Byte boxed = gson.fromJson("1", Byte.class);
assertEquals(1, (byte)boxed);
byte primitive = gson.fromJson("1", byte.class);
assertEquals(1, primitive);
byte[] bytes = gson.fromJson("[-128, 0, 127, 255]", byte[].class);
assertArrayEquals(new byte[] {-128, 0, 127, -1}, bytes);
}
public void testByteDeserializationLossy() {
try {
gson.fromJson("-129", byte.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Lossy conversion from -129 to byte; at path $", e.getMessage());
}
try {
gson.fromJson("256", byte.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Lossy conversion from 256 to byte; at path $", e.getMessage());
}
try {
gson.fromJson("2147483648", byte.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage());
}
}
public void testShortSerialization() {
assertEquals("1", gson.toJson(1, short.class));
assertEquals("1", gson.toJson(1, Short.class));
}
public void testByteDeserialization() {
Byte target = gson.fromJson("1", Byte.class);
assertEquals(1, (byte)target);
byte primitive = gson.fromJson("1", byte.class);
public void testShortDeserialization() {
Short boxed = gson.fromJson("1", Short.class);
assertEquals(1, (short)boxed);
short primitive = gson.fromJson("1", short.class);
assertEquals(1, primitive);
short[] shorts = gson.fromJson("[-32768, 0, 32767, 65535]", short[].class);
assertArrayEquals(new short[] {-32768, 0, 32767, -1}, shorts);
}
public void testShortDeserializationLossy() {
try {
gson.fromJson("-32769", short.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Lossy conversion from -32769 to short; at path $", e.getMessage());
}
try {
gson.fromJson("65536", short.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("Lossy conversion from 65536 to short; at path $", e.getMessage());
}
try {
gson.fromJson("2147483648", short.class);
fail();
} catch (JsonSyntaxException e) {
assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage());
}
}
public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() {

View File

@ -46,110 +46,154 @@ public class JsonReaderPathTest {
@Test public void path() throws IOException {
JsonReader reader = factory.create("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}");
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.beginObject();
assertEquals("$.", reader.getPreviousPath());
assertEquals("$.", reader.getPath());
reader.nextName();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.beginArray();
assertEquals("$.a[0]", reader.getPreviousPath());
assertEquals("$.a[0]", reader.getPath());
reader.nextInt();
assertEquals("$.a[0]", reader.getPreviousPath());
assertEquals("$.a[1]", reader.getPath());
reader.nextBoolean();
assertEquals("$.a[1]", reader.getPreviousPath());
assertEquals("$.a[2]", reader.getPath());
reader.nextBoolean();
assertEquals("$.a[2]", reader.getPreviousPath());
assertEquals("$.a[3]", reader.getPath());
reader.nextNull();
assertEquals("$.a[3]", reader.getPreviousPath());
assertEquals("$.a[4]", reader.getPath());
reader.nextString();
assertEquals("$.a[4]", reader.getPreviousPath());
assertEquals("$.a[5]", reader.getPath());
reader.beginObject();
assertEquals("$.a[5].", reader.getPreviousPath());
assertEquals("$.a[5].", reader.getPath());
reader.nextName();
assertEquals("$.a[5].c", reader.getPreviousPath());
assertEquals("$.a[5].c", reader.getPath());
reader.nextString();
assertEquals("$.a[5].c", reader.getPreviousPath());
assertEquals("$.a[5].c", reader.getPath());
reader.endObject();
assertEquals("$.a[5]", reader.getPreviousPath());
assertEquals("$.a[6]", reader.getPath());
reader.beginArray();
assertEquals("$.a[6][0]", reader.getPreviousPath());
assertEquals("$.a[6][0]", reader.getPath());
reader.nextInt();
assertEquals("$.a[6][0]", reader.getPreviousPath());
assertEquals("$.a[6][1]", reader.getPath());
reader.endArray();
assertEquals("$.a[6]", reader.getPreviousPath());
assertEquals("$.a[7]", reader.getPath());
reader.endArray();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.endObject();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}
@Test public void objectPath() throws IOException {
JsonReader reader = factory.create("{\"a\":1,\"b\":2}");
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.peek();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.beginObject();
assertEquals("$.", reader.getPreviousPath());
assertEquals("$.", reader.getPath());
reader.peek();
assertEquals("$.", reader.getPreviousPath());
assertEquals("$.", reader.getPath());
reader.nextName();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.peek();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.nextInt();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.peek();
assertEquals("$.a", reader.getPreviousPath());
assertEquals("$.a", reader.getPath());
reader.nextName();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
reader.peek();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
reader.nextInt();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
reader.peek();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
reader.endObject();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.peek();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.close();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}
@Test public void arrayPath() throws IOException {
JsonReader reader = factory.create("[1,2]");
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.peek();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.beginArray();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[0]", reader.getPath());
reader.peek();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[0]", reader.getPath());
reader.nextInt();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[1]", reader.getPath());
reader.peek();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[1]", reader.getPath());
reader.nextInt();
assertEquals("$[1]", reader.getPreviousPath());
assertEquals("$[2]", reader.getPath());
reader.peek();
assertEquals("$[1]", reader.getPreviousPath());
assertEquals("$[2]", reader.getPath());
reader.endArray();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.peek();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.close();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}
@ -160,9 +204,11 @@ public class JsonReaderPathTest {
reader.setLenient(true);
reader.beginArray();
reader.endArray();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
reader.beginArray();
reader.endArray();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}
@ -171,6 +217,7 @@ public class JsonReaderPathTest {
reader.beginArray();
reader.skipValue();
reader.skipValue();
assertEquals("$[1]", reader.getPreviousPath());
assertEquals("$[2]", reader.getPath());
}
@ -178,17 +225,21 @@ public class JsonReaderPathTest {
JsonReader reader = factory.create("{\"a\":1}");
reader.beginObject();
reader.skipValue();
assertEquals("$.null", reader.getPreviousPath());
assertEquals("$.null", reader.getPath());
}
@Test public void skipObjectValues() throws IOException {
JsonReader reader = factory.create("{\"a\":1,\"b\":2}");
reader.beginObject();
assertEquals("$.", reader.getPreviousPath());
assertEquals("$.", reader.getPath());
reader.nextName();
reader.skipValue();
assertEquals("$.null", reader.getPreviousPath());
assertEquals("$.null", reader.getPath());
reader.nextName();
assertEquals("$.b", reader.getPreviousPath());
assertEquals("$.b", reader.getPath());
}
@ -196,46 +247,63 @@ public class JsonReaderPathTest {
JsonReader reader = factory.create("[[1,2,3],4]");
reader.beginArray();
reader.skipValue();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[1]", reader.getPath());
}
@Test public void arrayOfObjects() throws IOException {
JsonReader reader = factory.create("[{},{},{}]");
reader.beginArray();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[0]", reader.getPath());
reader.beginObject();
assertEquals("$[0].", reader.getPreviousPath());
assertEquals("$[0].", reader.getPath());
reader.endObject();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[1]", reader.getPath());
reader.beginObject();
assertEquals("$[1].", reader.getPreviousPath());
assertEquals("$[1].", reader.getPath());
reader.endObject();
assertEquals("$[1]", reader.getPreviousPath());
assertEquals("$[2]", reader.getPath());
reader.beginObject();
assertEquals("$[2].", reader.getPreviousPath());
assertEquals("$[2].", reader.getPath());
reader.endObject();
assertEquals("$[2]", reader.getPreviousPath());
assertEquals("$[3]", reader.getPath());
reader.endArray();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}
@Test public void arrayOfArrays() throws IOException {
JsonReader reader = factory.create("[[],[],[]]");
reader.beginArray();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[0]", reader.getPath());
reader.beginArray();
assertEquals("$[0][0]", reader.getPreviousPath());
assertEquals("$[0][0]", reader.getPath());
reader.endArray();
assertEquals("$[0]", reader.getPreviousPath());
assertEquals("$[1]", reader.getPath());
reader.beginArray();
assertEquals("$[1][0]", reader.getPreviousPath());
assertEquals("$[1][0]", reader.getPath());
reader.endArray();
assertEquals("$[1]", reader.getPreviousPath());
assertEquals("$[2]", reader.getPath());
reader.beginArray();
assertEquals("$[2][0]", reader.getPreviousPath());
assertEquals("$[2][0]", reader.getPath());
reader.endArray();
assertEquals("$[2]", reader.getPreviousPath());
assertEquals("$[3]", reader.getPath());
reader.endArray();
assertEquals("$", reader.getPreviousPath());
assertEquals("$", reader.getPath());
}