languages`.
+
+## `IllegalStateException`: "Expected ... but was NULL"
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was NULL" is thrown
-**Reason:** You have written a custom `TypeAdapter` which does not properly handle a JSON null value
+**Reason:**
-**Solution:** Add code similar to the following at the beginning of the `read` method of your adapter:
+- A built-in adapter does not support JSON null values
+- You have written a custom `TypeAdapter` which does not properly handle JSON null values
+
+**Solution:** If this occurs for a custom adapter you wrote, add code similar to the following at the beginning of its `read` method:
```java
@Override
@@ -154,14 +197,14 @@ public MyClass read(JsonReader in) throws IOException {
in.nextNull();
return null;
}
-
+
...
}
```
Alternatively you can call [`nullSafe()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html#nullSafe()) on the adapter instance you created.
-## Properties missing in JSON
+## Properties missing in JSON
**Symptom:** Properties are missing in the JSON output
@@ -171,7 +214,7 @@ Alternatively you can call [`nullSafe()`](https://www.javadoc.io/doc/com.google.
Note: Gson does not support anonymous and local classes and will serialize them as JSON null, see the [related troubleshooting point](#null-values-for-anonymous-and-local-classes).
-## JSON output changes for newer Android versions
+## JSON output changes for newer Android versions
**Symptom:** The JSON output differs when running on newer Android versions
@@ -185,7 +228,7 @@ When no built-in adapter for a type exists and no custom adapter has been regist
If you want to prevent using reflection on third-party classes in the future you can write your own [`ReflectionAccessFilter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/ReflectionAccessFilter.html) or use one of the predefined ones, such as `ReflectionAccessFilter.BLOCK_ALL_PLATFORM`.
-## JSON output contains values of `static` fields
+## JSON output contains values of `static` fields
**Symptom:** The JSON output contains values of `static` fields
@@ -193,7 +236,7 @@ If you want to prevent using reflection on third-party classes in the future you
**Solution:** When calling `GsonBuilder.excludeFieldsWithModifiers` you overwrite the default excluded modifiers. Therefore, you have to explicitly exclude `static` fields if desired. This can be done by adding `Modifier.STATIC` as additional argument.
-## `NoSuchMethodError` when calling Gson methods
+## `NoSuchMethodError` when calling Gson methods
**Symptom:** A `java.lang.NoSuchMethodError` is thrown when trying to call certain Gson methods
@@ -210,3 +253,31 @@ System.out.println(Gson.class.getProtectionDomain().getCodeSource().getLocation(
```
If that fails with a `NullPointerException` you have to try one of the other ways to find out where a class is loaded from.
+
+## `IllegalArgumentException`: 'Class ... declares multiple JSON fields named '...''
+
+**Symptom:** An exception with the message 'Class ... declares multiple JSON fields named '...'' is thrown
+
+**Reason:**
+
+- The name you have specified with a [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) annotation for a field collides with the name of another field
+- The [`FieldNamingStrategy`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/FieldNamingStrategy.html) you have specified produces conflicting field names
+- A field of your class has the same name as the field of a superclass
+
+Gson prevents multiple fields with the same name because during deserialization it would be ambiguous for which field the JSON data should be deserialized. For serialization it would cause the same field to appear multiple times in JSON. While the JSON specification permits this, it is likely that the application parsing the JSON data will not handle it correctly.
+
+**Solution:** First identify the fields with conflicting names based on the exception message. Then decide if you want to rename one of them using the [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) annotation, or if you want to [exclude](UserGuide.md#excluding-fields-from-serialization-and-deserialization) one of them. When excluding one of the fields you have to include it for both serialization and deserialization (even if your application only performs one of these actions) because the duplicate field check cannot differentiate between these actions.
+
+## `UnsupportedOperationException` when serializing or deserializing `java.lang.Class`
+
+**Symptom:** An `UnsupportedOperationException` is thrown when trying to serialize or deserialize `java.lang.Class`
+
+**Reason:** Gson intentionally does not permit serializing and deserializing `java.lang.Class` for security reasons. Otherwise a malicious user could make your application load an arbitrary class from the classpath and, depending on what your application does with the `Class`, in the worst case perform a remote code execution attack.
+
+**Solution:** First check if you really need to serialize or deserialize a `Class`. Often it is possible to use string aliases and then map them to the known `Class`; you could write a custom [`TypeAdapter`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) to do this. If the `Class` values are not known in advance, try to introduce a common base class or interface for all these classes and then verify that the deserialized class is a subclass. For example assuming the base class is called `MyBaseClass`, your custom `TypeAdapter` should load the class like this:
+
+```java
+Class.forName(jsonString, false, getClass().getClassLoader()).asSubclass(MyBaseClass.class)
+```
+
+This will not initialize arbitrary classes, and it will throw a `ClassCastException` if the loaded class is not the same as or a subclass of `MyBaseClass`.
diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java
index e90831046f..ec5f69f63e 100644
--- a/gson/src/main/java/com/google/gson/Gson.java
+++ b/gson/src/main/java/com/google/gson/Gson.java
@@ -105,7 +105,7 @@
* See the Gson User Guide
* for a more complete set of examples.
*
- * Lenient JSON handling
+ * Lenient JSON handling
* For legacy reasons most of the {@code Gson} methods allow JSON data which does not
* comply with the JSON specification, regardless of whether {@link GsonBuilder#setLenient()}
* is used or not. If this behavior is not desired, the following workarounds can be used:
diff --git a/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java b/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java
new file mode 100644
index 0000000000..69c1b06d90
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java
@@ -0,0 +1,12 @@
+package com.google.gson.internal;
+
+public class TroubleshootingGuide {
+ private TroubleshootingGuide() {}
+
+ /**
+ * Creates a URL referring to the specified troubleshooting section.
+ */
+ public static String createUrl(String id) {
+ return "https://github.com/google/gson/blob/master/Troubleshooting.md#" + id;
+ }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java
index 9194fc33bf..6a43b817c9 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java
@@ -33,6 +33,7 @@
import com.google.gson.internal.ObjectConstructor;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.ReflectionAccessFilterHelper;
+import com.google.gson.internal.TroubleshootingGuide;
import com.google.gson.internal.reflect.ReflectionHelper;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
@@ -114,7 +115,7 @@ public TypeAdapter create(Gson gson, final TypeToken type) {
if (filterResult == FilterResult.BLOCK_ALL) {
throw new JsonIOException(
"ReflectionAccessFilter does not permit using reflection for " + raw
- + ". Register a TypeAdapter for this type or adjust the access filter.");
+ + ". Register a TypeAdapter for this type or adjust the access filter.");
}
boolean blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE;
@@ -306,7 +307,8 @@ private Map getBoundFields(Gson context, TypeToken> type,
if (previous != null) {
throw new IllegalArgumentException("Class " + originalRaw.getName()
+ " declares multiple JSON fields named '" + previous.name + "'; conflict is caused"
- + " by fields " + ReflectionHelper.fieldToString(previous.field) + " and " + ReflectionHelper.fieldToString(field));
+ + " by fields " + ReflectionHelper.fieldToString(previous.field) + " and " + ReflectionHelper.fieldToString(field)
+ + "\nSee " + TroubleshootingGuide.createUrl("duplicate-fields"));
}
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
index b76a0f0e3c..0f414e81e9 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
@@ -28,6 +28,7 @@
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.LazilyParsedNumber;
+import com.google.gson.internal.TroubleshootingGuide;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
@@ -73,12 +74,14 @@ private TypeAdapters() {
@Override
public void write(JsonWriter out, Class value) throws IOException {
throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
- + value.getName() + ". Forgot to register a type adapter?");
+ + value.getName() + ". Forgot to register a type adapter?"
+ + "\nSee " + TroubleshootingGuide.createUrl("java-lang-class-unsupported"));
}
@Override
public Class read(JsonReader in) throws IOException {
throw new UnsupportedOperationException(
- "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
+ "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"
+ + "\nSee " + TroubleshootingGuide.createUrl("java-lang-class-unsupported"));
}
}.nullSafe();
diff --git a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java
index 41dd4cf372..de1df3552d 100644
--- a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java
+++ b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java
@@ -18,6 +18,7 @@
import com.google.gson.JsonIOException;
import com.google.gson.internal.GsonBuildConfig;
+import com.google.gson.internal.TroubleshootingGuide;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -40,6 +41,17 @@ public class ReflectionHelper {
private ReflectionHelper() {}
+ private static String getInaccessibleTroubleshootingSuffix(Exception e) {
+ // Class was added in Java 9, therefore cannot use instanceof
+ if (e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
+ String message = e.getMessage();
+ String troubleshootingId = message != null && message.contains("to module com.google.gson")
+ ? "reflection-inaccessible-to-module-gson" : "reflection-inaccessible";
+ return "\nSee " + TroubleshootingGuide.createUrl(troubleshootingId);
+ }
+ return "";
+ }
+
/**
* Internal implementation of making an {@link AccessibleObject} accessible.
*
@@ -52,7 +64,8 @@ public static void makeAccessible(AccessibleObject object) throws JsonIOExceptio
} catch (Exception exception) {
String description = getAccessibleObjectDescription(object, false);
throw new JsonIOException("Failed making " + description + " accessible; either increase its visibility"
- + " or write a custom TypeAdapter for its declaring type.", exception);
+ + " or write a custom TypeAdapter for its declaring type." + getInaccessibleTroubleshootingSuffix(exception),
+ exception);
}
}
@@ -142,7 +155,7 @@ public static String tryMakeAccessible(Constructor> constructor) {
return "Failed making constructor '" + constructorToString(constructor) + "' accessible;"
+ " either increase its visibility or write a custom InstanceCreator or TypeAdapter for"
// Include the message since it might contain more detailed information
- + " its declaring type: " + exception.getMessage();
+ + " its declaring type: " + exception.getMessage() + getInaccessibleTroubleshootingSuffix(exception);
}
}
diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java
index e0931320d1..de7aef5ff5 100644
--- a/gson/src/main/java/com/google/gson/stream/JsonReader.java
+++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java
@@ -17,6 +17,7 @@
package com.google.gson.stream;
import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.internal.TroubleshootingGuide;
import com.google.gson.internal.bind.JsonTreeReader;
import java.io.Closeable;
import java.io.EOFException;
@@ -355,7 +356,7 @@ public void beginArray() throws IOException {
pathIndices[stackSize - 1] = 0;
peeked = PEEKED_NONE;
} else {
- throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() + locationString());
+ throw unexpectedTokenError("BEGIN_ARRAY");
}
}
@@ -373,7 +374,7 @@ public void endArray() throws IOException {
pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE;
} else {
- throw new IllegalStateException("Expected END_ARRAY but was " + peek() + locationString());
+ throw unexpectedTokenError("END_ARRAY");
}
}
@@ -390,7 +391,7 @@ public void beginObject() throws IOException {
push(JsonScope.EMPTY_OBJECT);
peeked = PEEKED_NONE;
} else {
- throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
+ throw unexpectedTokenError("BEGIN_OBJECT");
}
}
@@ -409,7 +410,7 @@ public void endObject() throws IOException {
pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE;
} else {
- throw new IllegalStateException("Expected END_OBJECT but was " + peek() + locationString());
+ throw unexpectedTokenError("END_OBJECT");
}
}
@@ -797,7 +798,7 @@ public String nextName() throws IOException {
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
result = nextQuotedValue('"');
} else {
- throw new IllegalStateException("Expected a name but was " + peek() + locationString());
+ throw unexpectedTokenError("a name");
}
peeked = PEEKED_NONE;
pathNames[stackSize - 1] = result;
@@ -833,7 +834,7 @@ public String nextString() throws IOException {
result = new String(buffer, pos, peekedNumberLength);
pos += peekedNumberLength;
} else {
- throw new IllegalStateException("Expected a string but was " + peek() + locationString());
+ throw unexpectedTokenError("a string");
}
peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++;
@@ -861,7 +862,7 @@ public boolean nextBoolean() throws IOException {
pathIndices[stackSize - 1]++;
return false;
}
- throw new IllegalStateException("Expected a boolean but was " + peek() + locationString());
+ throw unexpectedTokenError("a boolean");
}
/**
@@ -880,7 +881,7 @@ public void nextNull() throws IOException {
peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++;
} else {
- throw new IllegalStateException("Expected null but was " + peek() + locationString());
+ throw unexpectedTokenError("null");
}
}
@@ -915,14 +916,13 @@ public double nextDouble() throws IOException {
} else if (p == PEEKED_UNQUOTED) {
peekedString = nextUnquotedValue();
} else if (p != PEEKED_BUFFERED) {
- throw new IllegalStateException("Expected a double but was " + peek() + locationString());
+ throw unexpectedTokenError("a double");
}
peeked = PEEKED_BUFFERED;
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
- throw new MalformedJsonException(
- "JSON forbids NaN and infinities: " + result + locationString());
+ throw syntaxError("JSON forbids NaN and infinities: " + result);
}
peekedString = null;
peeked = PEEKED_NONE;
@@ -970,7 +970,7 @@ public long nextLong() throws IOException {
// Fall back to parse as a double below.
}
} else {
- throw new IllegalStateException("Expected a long but was " + peek() + locationString());
+ throw unexpectedTokenError("a long");
}
peeked = PEEKED_BUFFERED;
@@ -1208,7 +1208,7 @@ public int nextInt() throws IOException {
// Fall back to parse as a double below.
}
} else {
- throw new IllegalStateException("Expected an int but was " + peek() + locationString());
+ throw unexpectedTokenError("an int");
}
peeked = PEEKED_BUFFERED;
@@ -1584,10 +1584,10 @@ public String getPath() {
/**
* Unescapes the character identified by the character or characters that
* immediately follow a backslash. The backslash '\' should have already
- * been read. This supports both unicode escapes "u000A" and two-character
+ * been read. This supports both Unicode escapes "u000A" and two-character
* escapes "\n".
*
- * @throws MalformedJsonException if any unicode escape sequences are
+ * @throws MalformedJsonException if any Unicode escape sequences are
* malformed.
*/
@SuppressWarnings("fallthrough")
@@ -1614,7 +1614,7 @@ private char readEscapeCharacter() throws IOException {
} else if (c >= 'A' && c <= 'F') {
result += (c - 'A' + 10);
} else {
- throw new MalformedJsonException("\\u" + new String(buffer, pos, 4));
+ throw syntaxError("Malformed Unicode escape \\u" + new String(buffer, pos, 4));
}
}
pos += 4;
@@ -1656,7 +1656,16 @@ private char readEscapeCharacter() throws IOException {
* with this reader's content.
*/
private IOException syntaxError(String message) throws IOException {
- throw new MalformedJsonException(message + locationString());
+ throw new MalformedJsonException(message + locationString()
+ + "\nSee " + TroubleshootingGuide.createUrl("malformed-json"));
+ }
+
+ private IllegalStateException unexpectedTokenError(String expected) throws IOException {
+ JsonToken peeked = peek();
+ String troubleshootingId = peeked == JsonToken.NULL
+ ? "adapter-not-null-safe" : "unexpected-json-structure";
+ return new IllegalStateException("Expected " + expected + " but was " + peek() + locationString()
+ + "\nSee " + TroubleshootingGuide.createUrl(troubleshootingId));
}
/**
@@ -1699,8 +1708,7 @@ private void consumeNonExecutePrefix() throws IOException {
} else if (p == PEEKED_UNQUOTED_NAME) {
reader.peeked = PEEKED_UNQUOTED;
} else {
- throw new IllegalStateException(
- "Expected a name but was " + reader.peek() + reader.locationString());
+ throw reader.unexpectedTokenError("a name");
}
}
};
diff --git a/gson/src/test/java/com/google/gson/JsonArrayTest.java b/gson/src/test/java/com/google/gson/JsonArrayTest.java
index c3c0184fd6..21a5d58401 100644
--- a/gson/src/test/java/com/google/gson/JsonArrayTest.java
+++ b/gson/src/test/java/com/google/gson/JsonArrayTest.java
@@ -17,7 +17,6 @@
package com.google.gson;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import com.google.common.testing.EqualsTester;
@@ -140,22 +139,19 @@ public void testFailedGetArrayValues() {
jsonArray.getAsBoolean();
fail("expected getBoolean to fail");
} catch (UnsupportedOperationException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("JsonObject");
+ assertThat(e).hasMessageThat().isEqualTo("JsonObject");
}
try {
jsonArray.get(-1);
fail("expected get to fail");
} catch (IndexOutOfBoundsException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("Index -1 out of bounds for length 1");
+ assertThat(e).hasMessageThat().isEqualTo("Index -1 out of bounds for length 1");
}
try {
jsonArray.getAsString();
fail("expected getString to fail");
} catch (UnsupportedOperationException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("JsonObject");
+ assertThat(e).hasMessageThat().isEqualTo("JsonObject");
}
jsonArray.remove(0);
@@ -164,36 +160,31 @@ public void testFailedGetArrayValues() {
jsonArray.getAsDouble();
fail("expected getDouble to fail");
} catch (NumberFormatException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
+ assertThat(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
}
try {
jsonArray.getAsInt();
fail("expected getInt to fail");
} catch (NumberFormatException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
+ assertThat(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
}
try {
jsonArray.get(0).getAsJsonArray();
fail("expected getJSONArray to fail");
} catch (IllegalStateException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("Not a JSON Array: \"hello\"");
+ assertThat(e).hasMessageThat().isEqualTo("Not a JSON Array: \"hello\"");
}
try {
jsonArray.getAsJsonObject();
fail("expected getJSONObject to fail");
} catch (IllegalStateException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo( "Not a JSON Object: [\"hello\"]");
+ assertThat(e).hasMessageThat().isEqualTo("Not a JSON Object: [\"hello\"]");
}
try {
jsonArray.getAsLong();
fail("expected getLong to fail");
} catch (NumberFormatException e) {
- assertWithMessage("Expected an exception message")
- .that(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
+ assertThat(e).hasMessageThat().isEqualTo("For input string: \"hello\"");
}
}
diff --git a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java
index 4750d24fae..2e40867f8f 100644
--- a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java
+++ b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java
@@ -37,7 +37,9 @@ public void testDouble() throws IOException {
strategy.readNumber(fromString("1e400"));
fail();
} catch (MalformedJsonException expected) {
- assertThat(expected).hasMessageThat().isEqualTo("JSON forbids NaN and infinities: Infinity at line 1 column 6 path $");
+ assertThat(expected).hasMessageThat().isEqualTo(
+ "JSON forbids NaN and infinities: Infinity at line 1 column 6 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
try {
strategy.readNumber(fromString("\"not-a-number\""));
@@ -80,19 +82,25 @@ public void testLongOrDouble() throws IOException {
strategy.readNumber(fromString("NaN"));
fail();
} catch (MalformedJsonException expected) {
- assertThat(expected).hasMessageThat().isEqualTo("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $");
+ assertThat(expected).hasMessageThat().isEqualTo(
+ "Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
try {
strategy.readNumber(fromString("Infinity"));
fail();
} catch (MalformedJsonException expected) {
- assertThat(expected).hasMessageThat().isEqualTo("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $");
+ assertThat(expected).hasMessageThat().isEqualTo(
+ "Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
try {
strategy.readNumber(fromString("-Infinity"));
fail();
} catch (MalformedJsonException expected) {
- assertThat(expected).hasMessageThat().isEqualTo("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $");
+ assertThat(expected).hasMessageThat().isEqualTo(
+ "Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -117,21 +125,29 @@ public void testNullsAreNeverExpected() throws IOException {
ToNumberPolicy.DOUBLE.readNumber(fromString("null"));
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected a double but was NULL at line 1 column 5 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#adapter-not-null-safe");
}
try {
ToNumberPolicy.LAZILY_PARSED_NUMBER.readNumber(fromString("null"));
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected a string but was NULL at line 1 column 5 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#adapter-not-null-safe");
}
try {
ToNumberPolicy.LONG_OR_DOUBLE.readNumber(fromString("null"));
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected a string but was NULL at line 1 column 5 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#adapter-not-null-safe");
}
try {
ToNumberPolicy.BIG_DECIMAL.readNumber(fromString("null"));
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected a string but was NULL at line 1 column 5 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#adapter-not-null-safe");
}
}
diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java
index d0d2eff78c..841b01251e 100644
--- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java
+++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java
@@ -95,6 +95,8 @@ public void testClassSerialization() {
gson.toJson(String.class);
fail();
} catch (UnsupportedOperationException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Attempted to serialize java.lang.Class: java.lang.String. Forgot to register a type adapter?"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#java-lang-class-unsupported");
}
// Override with a custom type adapter for class.
gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create();
@@ -107,6 +109,8 @@ public void testClassDeserialization() {
gson.fromJson("String.class", Class.class);
fail();
} catch (UnsupportedOperationException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#java-lang-class-unsupported");
}
// Override with a custom type adapter for class.
gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create();
@@ -365,7 +369,7 @@ public void testBitSetDeserialization() {
gson.fromJson("[1, []]", BitSet.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("Invalid bitset value type: BEGIN_ARRAY; at path $[1]");
+ assertThat(e).hasMessageThat().isEqualTo("Invalid bitset value type: BEGIN_ARRAY; at path $[1]");
}
try {
@@ -631,7 +635,7 @@ public void testJsonElementTypeMismatch() {
gson.fromJson("\"abc\"", JsonObject.class);
fail();
} catch (JsonSyntaxException expected) {
- assertThat(expected.getMessage()).isEqualTo("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $");
+ assertThat(expected).hasMessageThat().isEqualTo("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $");
}
}
diff --git a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java
index b7029487e8..a1a048fcd7 100644
--- a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java
+++ b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java
@@ -53,8 +53,8 @@ public void setUp() {
@Test
public void testVersionPattern() {
- assertThat(GSON_VERSION_PATTERN.matcher("(GSON 2.8.5)").matches()).isTrue();
- assertThat(GSON_VERSION_PATTERN.matcher("(GSON 2.8.5-SNAPSHOT)").matches()).isTrue();
+ assertThat("(GSON 2.8.5)").matches(GSON_VERSION_PATTERN);
+ assertThat("(GSON 2.8.5-SNAPSHOT)").matches(GSON_VERSION_PATTERN);
}
@Test
@@ -80,7 +80,7 @@ private void ensureAssertionErrorPrintsGsonVersion(AssertionError expected) {
assertThat(end > 0 && end > start + 6).isTrue();
String version = msg.substring(start, end);
// System.err.println(version);
- assertThat(GSON_VERSION_PATTERN.matcher(version).matches()).isTrue();
+ assertThat(version).matches(GSON_VERSION_PATTERN);
}
private static final class TestType {
diff --git a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java
index 38a45644ba..5d863f1538 100644
--- a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java
+++ b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java
@@ -59,7 +59,7 @@ public void testSerializeComplexMapWithTypeAdapter() {
@Test
@Ignore
- public void disabled_testTwoTypesCollapseToOneSerialize() {
+ public void testTwoTypesCollapseToOneSerialize() {
Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
diff --git a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java
index 25a5b5ff84..f360e84d97 100644
--- a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java
+++ b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java
@@ -17,7 +17,6 @@
package com.google.gson.functional;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
@@ -57,8 +56,8 @@ public void testListOfSubclassFields() {
list.add(new Sub(2, 3));
ClassWithContainersOfBaseFields target = new ClassWithContainersOfBaseFields(list, null);
String json = gson.toJson(target);
- assertWithMessage(json).that(json).contains("{\"b\":1}");
- assertWithMessage(json).that(json).contains("{\"s\":3,\"b\":2}");
+ assertThat(json).contains("{\"b\":1}");
+ assertThat(json).contains("{\"s\":3,\"b\":2}");
}
@Test
@@ -98,8 +97,8 @@ public void testListOfParameterizedSubclassFields() {
ClassWithContainersOfParameterizedBaseFields target =
new ClassWithContainersOfParameterizedBaseFields(list, null);
String json = gson.toJson(target);
- assertWithMessage(json).that(json).contains("{\"t\":\"one\"}");
- assertWithMessage(json).that(json).doesNotContain("\"s\":");
+ assertThat(json).contains("{\"t\":\"one\"}");
+ assertThat(json).doesNotContain("\"s\":");
}
/**
diff --git a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java
index 5527d36381..8234df8ecc 100644
--- a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java
+++ b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java
@@ -26,6 +26,7 @@
import com.google.gson.common.TestTypes.ClassWithSerializedNameFields;
import com.google.gson.common.TestTypes.StringWrapper;
import java.lang.reflect.Field;
+import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
@@ -137,7 +138,29 @@ public void testGsonDuplicateNameUsingSerializedNameFieldNamingPolicySerializati
assertThat(expected).hasMessageThat()
.isEqualTo("Class com.google.gson.functional.NamingPolicyTest$ClassWithDuplicateFields declares multiple JSON fields named 'a';"
+ " conflict is caused by fields com.google.gson.functional.NamingPolicyTest$ClassWithDuplicateFields#a and"
- + " com.google.gson.functional.NamingPolicyTest$ClassWithDuplicateFields#b");
+ + " com.google.gson.functional.NamingPolicyTest$ClassWithDuplicateFields#b"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#duplicate-fields");
+ }
+ }
+
+ @Test
+ public void testGsonDuplicateNameDueToBadNamingPolicy() {
+ Gson gson = builder.setFieldNamingStrategy(new FieldNamingStrategy() {
+ @Override
+ public String translateName(Field f) {
+ return "x";
+ }
+ }).create();
+
+ try {
+ gson.toJson(new ClassWithTwoFields());
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected).hasMessageThat()
+ .isEqualTo("Class com.google.gson.functional.NamingPolicyTest$ClassWithTwoFields declares multiple JSON fields named 'x';"
+ + " conflict is caused by fields com.google.gson.functional.NamingPolicyTest$ClassWithTwoFields#a and"
+ + " com.google.gson.functional.NamingPolicyTest$ClassWithTwoFields#b"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#duplicate-fields");
}
}
@@ -209,7 +232,7 @@ static final class AtName {
private static final class UpperCaseNamingStrategy implements FieldNamingStrategy {
@Override
public String translateName(Field f) {
- return f.getName().toUpperCase();
+ return f.getName().toUpperCase(Locale.ROOT);
}
}
@@ -239,4 +262,12 @@ private static class ClassWithComplexFieldName {
this.value = value;
}
}
+
+ @SuppressWarnings("unused")
+ private static class ClassWithTwoFields {
+ public int a;
+ public int b;
+
+ public ClassWithTwoFields() {}
+ }
}
diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java
index 208db3a01e..46324e98d8 100644
--- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java
+++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java
@@ -177,7 +177,8 @@ public void testClassWithDuplicateFields() {
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("Class com.google.gson.functional.ObjectTest$Subclass declares multiple JSON fields named 's';"
+ " conflict is caused by fields com.google.gson.functional.ObjectTest$Superclass1#s and"
- + " com.google.gson.functional.ObjectTest$Superclass2#s");
+ + " com.google.gson.functional.ObjectTest$Superclass2#s"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#duplicate-fields");
}
}
@@ -196,6 +197,7 @@ public void testNestedDeserialization() {
Nested target = gson.fromJson(json, Nested.class);
assertThat(target.getExpectedJson()).isEqualTo(json);
}
+
@Test
public void testNullSerialization() {
assertThat(gson.toJson(null)).isEqualTo("null");
@@ -632,7 +634,7 @@ public void testStaticFieldDeserialization() {
gson.fromJson("{\"s\":\"custom\"}", ClassWithStaticFinalField.class);
fail();
} catch (JsonIOException e) {
- assertThat( e.getMessage()).isEqualTo("Cannot set value of 'static final' field 'com.google.gson.functional.ObjectTest$ClassWithStaticFinalField#s'");
+ assertThat(e).hasMessageThat().isEqualTo("Cannot set value of 'static final' field 'com.google.gson.functional.ObjectTest$ClassWithStaticFinalField#s'");
}
}
@@ -652,7 +654,7 @@ public void testThrowingDefaultConstructor() {
}
// TODO: Adjust this once Gson throws more specific exception type
catch (RuntimeException e) {
- assertThat( e.getMessage()).isEqualTo("Failed to invoke constructor 'com.google.gson.functional.ObjectTest$ClassWithThrowingConstructor()' with no args");
+ assertThat(e).hasMessageThat().isEqualTo("Failed to invoke constructor 'com.google.gson.functional.ObjectTest$ClassWithThrowingConstructor()' with no args");
assertThat(e).hasCauseThat().isSameInstanceAs(ClassWithThrowingConstructor.thrownException);
}
}
diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java
index f7bccb46e3..244d288371 100644
--- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java
+++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java
@@ -92,21 +92,21 @@ public void testByteDeserializationLossy() {
gson.fromJson("-129", byte.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("Lossy conversion from -129 to byte; at path $");
+ assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -129 to byte; at path $");
}
try {
gson.fromJson("256", byte.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("Lossy conversion from 256 to byte; at path $");
+ assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 256 to byte; at path $");
}
try {
gson.fromJson("2147483648", byte.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $");
+ assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $");
}
}
@@ -140,21 +140,21 @@ public void testShortDeserializationLossy() {
gson.fromJson("-32769", short.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("Lossy conversion from -32769 to short; at path $");
+ assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -32769 to short; at path $");
}
try {
gson.fromJson("65536", short.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("Lossy conversion from 65536 to short; at path $");
+ assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 65536 to short; at path $");
}
try {
gson.fromJson("2147483648", short.class);
fail();
} catch (JsonSyntaxException e) {
- assertThat(e.getMessage()).isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $");
+ assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $");
}
}
@@ -1064,6 +1064,7 @@ public void testDeserializingBigIntegerAsBigDecimal() {
@Test
public void testStringsAsBooleans() {
String json = "['true', 'false', 'TRUE', 'yes', '1']";
- assertThat( gson.>fromJson(json, new TypeToken>() {}.getType())).isEqualTo(Arrays.asList(true, false, true, false, false));
+ List deserialized = gson.fromJson(json, new TypeToken>() {});
+ assertThat(deserialized).isEqualTo(Arrays.asList(true, false, true, false, false));
}
}
diff --git a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java
index 756603607b..bd0c71983c 100644
--- a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java
+++ b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java
@@ -79,7 +79,7 @@ public void checkPermission(Permission perm) {
gson.getAdapter(clazz);
fail();
} catch (SecurityException e) {
- assertThat(e.getMessage()).isEqualTo("Gson: no-member-access");
+ assertThat(e).hasMessageThat().isEqualTo("Gson: no-member-access");
}
final AtomicBoolean wasReadCalled = new AtomicBoolean(false);
@@ -107,6 +107,20 @@ public Object read(JsonReader in) throws IOException {
}
}
+ private static JsonIOException assertInaccessibleException(String json, Class> toDeserialize) {
+ Gson gson = new Gson();
+ try {
+ gson.fromJson(json, toDeserialize);
+ throw new AssertionError("Missing exception; test has to be run with `--illegal-access=deny`");
+ } catch (JsonSyntaxException e) {
+ throw new AssertionError("Unexpected exception; test has to be run with `--illegal-access=deny`", e);
+ } catch (JsonIOException expected) {
+ assertThat(expected).hasMessageThat().endsWith("\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#reflection-inaccessible");
+ // Return exception for further assertions
+ return expected;
+ }
+ }
+
/**
* Test serializing an instance of a non-accessible internal class, but where
* Gson supports serializing one of its superinterfaces.
@@ -126,14 +140,19 @@ public void testSerializeInternalImplementationObject() {
// But deserialization should fail
Class> internalClass = Collections.emptyList().getClass();
- try {
- gson.fromJson("[]", internalClass);
- fail("Missing exception; test has to be run with `--illegal-access=deny`");
- } catch (JsonSyntaxException e) {
- throw new AssertionError("Unexpected exception; test has to be run with `--illegal-access=deny`", e);
- } catch (JsonIOException expected) {
- assertThat(expected).hasMessageThat().startsWith("Failed making constructor 'java.util.Collections$EmptyList()' accessible;"
- + " either increase its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: ");
- }
+ JsonIOException exception = assertInaccessibleException("[]", internalClass);
+ // Don't check exact class name because it is a JDK implementation detail
+ assertThat(exception).hasMessageThat().startsWith("Failed making constructor '");
+ assertThat(exception).hasMessageThat().contains("' accessible; either increase its visibility or"
+ + " write a custom InstanceCreator or TypeAdapter for its declaring type: ");
+ }
+
+ @Test
+ public void testInaccessibleField() {
+ JsonIOException exception = assertInaccessibleException("{}", Throwable.class);
+ // Don't check exact field name because it is a JDK implementation detail
+ assertThat(exception).hasMessageThat().startsWith("Failed making field 'java.lang.Throwable#");
+ assertThat(exception).hasMessageThat().contains("' accessible; either increase its visibility or"
+ + " write a custom TypeAdapter for its declaring type.");
}
}
diff --git a/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java
index fecc828f47..d259564eb3 100644
--- a/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java
+++ b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java
@@ -17,7 +17,6 @@
package com.google.gson.functional;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import com.google.common.base.Splitter;
@@ -151,7 +150,7 @@ public void testSerialize1dArray() {
public void testDeserialize1dArray() throws IOException {
TypeAdapter arrayAdapter = miniGson.getAdapter(new TypeToken() {});
double[] array = arrayAdapter.fromJson("[1.0,2.0,3.0]");
- assertWithMessage(Arrays.toString(array)).that(Arrays.equals(new double[]{1.0, 2.0, 3.0}, array)).isTrue();
+ assertThat(array).isEqualTo(new double[]{1.0, 2.0, 3.0});
}
@Test
@@ -166,7 +165,7 @@ public void testDeserialize2dArray() throws IOException {
TypeAdapter arrayAdapter = miniGson.getAdapter(new TypeToken() {});
double[][] array = arrayAdapter.fromJson("[[1.0,2.0],[3.0]]");
double[][] expected = { {1.0, 2.0 }, { 3.0 } };
- assertWithMessage(Arrays.toString(array)).that(Arrays.deepEquals(expected, array)).isTrue();
+ assertThat(array).isEqualTo(expected);
}
@Test
@@ -195,7 +194,10 @@ public void testNullSafe() {
try {
gson.fromJson(json, Truck.class);
fail();
- } catch (JsonSyntaxException expected) {}
+ } catch (JsonSyntaxException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("java.lang.IllegalStateException: Expected a string but was NULL at line 1 column 33 path $.passengers[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#adapter-not-null-safe");
+ }
gson = new GsonBuilder().registerTypeAdapter(Person.class, typeAdapter.nullSafe()).create();
assertThat(gson.toJson(truck, Truck.class))
.isEqualTo("{\"horsePower\":1.0,\"passengers\":[null,\"jesse,30\"]}");
@@ -216,7 +218,7 @@ public void testSerializeRecursive() {
+ "'left':{'label':'left','left':null,'right':null},"
+ "'right':{'label':'right','left':null,'right':null}}");
}
-
+
@Test
public void testFromJsonTree() {
JsonObject truckObject = new JsonObject();
diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java
index e96175a3c9..328b6ede1e 100644
--- a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java
+++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java
@@ -75,7 +75,7 @@ public void testStreamingFollowedByNonstreaming() {
.registerTypeAdapter(Foo.class, newDeserializer("deserializer"))
.create();
assertThat(gson.toJson(new Foo("foo"))).isEqualTo("\"foo via serializer\"");
- assertThat( gson.fromJson("foo", Foo.class).name).isEqualTo("foo via deserializer");
+ assertThat(gson.fromJson("foo", Foo.class).name).isEqualTo("foo via deserializer");
}
@Test
diff --git a/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java
index f79c76b7cc..b3b7ff53d0 100644
--- a/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java
+++ b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java
@@ -99,7 +99,7 @@ public void testPutNullValue_Forbidden() {
map.put("a", null);
fail();
} catch (NullPointerException e) {
- assertThat(e.getMessage()).isEqualTo("value == null");
+ assertThat(e).hasMessageThat().isEqualTo("value == null");
}
assertThat(map).hasSize(0);
assertThat(map).doesNotContainKey("a");
@@ -132,7 +132,7 @@ public void testEntrySetValueNull_Forbidden() {
entry.setValue(null);
fail();
} catch (NullPointerException e) {
- assertThat(e.getMessage()).isEqualTo("value == null");
+ assertThat(e).hasMessageThat().isEqualTo("value == null");
}
assertThat(entry.getValue()).isEqualTo("1");
assertThat(map.get("a")).isEqualTo("1");
diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java
index 9974da13ce..4ee7656147 100644
--- a/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java
+++ b/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java
@@ -63,21 +63,21 @@ public void testStrictNansAndInfinities() throws IOException {
reader.nextDouble();
fail();
} catch (MalformedJsonException e) {
- assertThat(e.getMessage()).isEqualTo("JSON forbids NaN and infinities: NaN");
+ assertThat(e).hasMessageThat().isEqualTo("JSON forbids NaN and infinities: NaN");
}
assertThat(reader.nextString()).isEqualTo("NaN");
try {
reader.nextDouble();
fail();
} catch (MalformedJsonException e) {
- assertThat(e.getMessage()).isEqualTo("JSON forbids NaN and infinities: -Infinity");
+ assertThat(e).hasMessageThat().isEqualTo("JSON forbids NaN and infinities: -Infinity");
}
assertThat(reader.nextString()).isEqualTo("-Infinity");
try {
reader.nextDouble();
fail();
} catch (MalformedJsonException e) {
- assertThat(e.getMessage()).isEqualTo("JSON forbids NaN and infinities: Infinity");
+ assertThat(e).hasMessageThat().isEqualTo("JSON forbids NaN and infinities: Infinity");
}
assertThat(reader.nextString()).isEqualTo("Infinity");
reader.endArray();
diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java
index 5faa718015..cea6023c61 100644
--- a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java
+++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java
@@ -133,7 +133,7 @@ public JsonElement deepCopy() {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
- assertThat(expected.getMessage()).isEqualTo("Custom JsonElement subclass " + CustomSubclass.class.getName() + " is not supported");
+ assertThat(expected).hasMessageThat().isEqualTo("Custom JsonElement subclass " + CustomSubclass.class.getName() + " is not supported");
}
}
diff --git a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java
index a9b5ca1039..e864000950 100644
--- a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java
+++ b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java
@@ -34,7 +34,7 @@
/**
* Tests to measure performance for Gson. All tests in this file will be disabled in code. To run
- * them remove disabled_ prefix from the tests and run them.
+ * them remove the {@code @Ignore} annotation from the tests.
*
* @author Inderjeet Singh
* @author Joel Leitch
@@ -58,7 +58,7 @@ public void testDummy() {
@Test
@Ignore
- public void disabled_testStringDeserialization() {
+ public void testStringDeserialization() {
StringBuilder sb = new StringBuilder(8096);
sb.append("Error Yippie");
@@ -117,7 +117,7 @@ private CollectionEntry() {
*/
@Test
@Ignore
- public void disabled_testLargeCollectionSerialization() {
+ public void testLargeCollectionSerialization() {
int count = 1400000;
List list = new ArrayList<>(count);
for (int i = 0; i < count; ++i) {
@@ -131,7 +131,7 @@ public void disabled_testLargeCollectionSerialization() {
*/
@Test
@Ignore
- public void disabled_testLargeCollectionDeserialization() {
+ public void testLargeCollectionDeserialization() {
StringBuilder sb = new StringBuilder();
int count = 87000;
boolean first = true;
@@ -157,7 +157,7 @@ public void disabled_testLargeCollectionDeserialization() {
// Last I tested, Gson was able to serialize upto 14MB byte array
@Test
@Ignore
- public void disabled_testByteArraySerialization() {
+ public void testByteArraySerialization() {
for (int size = 4145152; true; size += 1036288) {
byte[] ba = new byte[size];
for (int i = 0; i < size; ++i) {
@@ -174,7 +174,7 @@ public void disabled_testByteArraySerialization() {
// Last I tested, Gson was able to deserialize a byte array of 11MB
@Test
@Ignore
- public void disabled_testByteArrayDeserialization() {
+ public void testByteArrayDeserialization() {
for (int numElements = 10639296; true; numElements += 16384) {
StringBuilder sb = new StringBuilder(numElements*2);
sb.append("[");
@@ -205,7 +205,7 @@ public void disabled_testByteArrayDeserialization() {
@Test
@Ignore
- public void disabled_testSerializeClasses() {
+ public void testSerializeClasses() {
ClassWithList c = new ClassWithList("str");
for (int i = 0; i < COLLECTION_SIZE; ++i) {
c.list.add(new ClassWithField("element-" + i));
@@ -222,7 +222,7 @@ public void disabled_testSerializeClasses() {
@Test
@Ignore
- public void disabled_testDeserializeClasses() {
+ public void testDeserializeClasses() {
String json = buildJsonForClassWithList();
ClassWithList[] target = new ClassWithList[NUM_ITERATIONS];
long t1 = System.currentTimeMillis();
@@ -236,7 +236,7 @@ public void disabled_testDeserializeClasses() {
@Test
@Ignore
- public void disabled_testLargeObjectSerializationAndDeserialization() {
+ public void testLargeObjectSerializationAndDeserialization() {
Map largeObject = new HashMap<>();
for (long l = 0; l < 100000; l++) {
largeObject.put("field" + l, l);
@@ -256,7 +256,7 @@ public void disabled_testLargeObjectSerializationAndDeserialization() {
@Test
@Ignore
- public void disabled_testSerializeExposedClasses() {
+ public void testSerializeExposedClasses() {
ClassWithListOfObjects c1 = new ClassWithListOfObjects("str");
for (int i1 = 0; i1 < COLLECTION_SIZE; ++i1) {
c1.list.add(new ClassWithExposedField("element-" + i1));
@@ -274,7 +274,7 @@ public void disabled_testSerializeExposedClasses() {
@Test
@Ignore
- public void disabled_testDeserializeExposedClasses() {
+ public void testDeserializeExposedClasses() {
String json = buildJsonForClassWithList();
ClassWithListOfObjects[] target = new ClassWithListOfObjects[NUM_ITERATIONS];
long t1 = System.currentTimeMillis();
@@ -288,7 +288,7 @@ public void disabled_testDeserializeExposedClasses() {
@Test
@Ignore
- public void disabled_testLargeGsonMapRoundTrip() throws Exception {
+ public void testLargeGsonMapRoundTrip() throws Exception {
Map original = new HashMap<>();
for (long i = 0; i < 1000000; i++) {
original.put(i, i + 1);
diff --git a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java
index 5df759f00b..61c5dfc2b9 100644
--- a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java
+++ b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java
@@ -235,21 +235,21 @@ class SubSubTypeToken2 extends SubTypeToken {}
new SubTypeToken() {};
fail();
} catch (IllegalStateException expected) {
- assertThat(expected.getMessage()).isEqualTo("Must only create direct subclasses of TypeToken");
+ assertThat(expected).hasMessageThat().isEqualTo("Must only create direct subclasses of TypeToken");
}
try {
new SubSubTypeToken1();
fail();
} catch (IllegalStateException expected) {
- assertThat(expected.getMessage()).isEqualTo("Must only create direct subclasses of TypeToken");
+ assertThat(expected).hasMessageThat().isEqualTo("Must only create direct subclasses of TypeToken");
}
try {
new SubSubTypeToken2();
fail();
} catch (IllegalStateException expected) {
- assertThat(expected.getMessage()).isEqualTo("Must only create direct subclasses of TypeToken");
+ assertThat(expected).hasMessageThat().isEqualTo("Must only create direct subclasses of TypeToken");
}
}
diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
index 2009f16bd6..90406b6df0 100644
--- a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
+++ b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
@@ -16,6 +16,7 @@
package com.google.gson.stream;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
import static com.google.gson.stream.JsonToken.BOOLEAN;
@@ -25,7 +26,6 @@
import static com.google.gson.stream.JsonToken.NULL;
import static com.google.gson.stream.JsonToken.NUMBER;
import static com.google.gson.stream.JsonToken.STRING;
-import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import java.io.EOFException;
@@ -273,7 +273,9 @@ public void testInvalidJsonInput() throws IOException {
try {
reader.nextName();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Invalid escape sequence at line 2 column 8 path $."
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -288,16 +290,16 @@ public void testNulls() {
}
@Test
- public void testEmptyString() {
+ public void testEmptyString() throws IOException {
try {
new JsonReader(reader("")).beginArray();
fail();
- } catch (IOException expected) {
+ } catch (EOFException expected) {
}
try {
new JsonReader(reader("")).beginObject();
fail();
- } catch (IOException expected) {
+ } catch (EOFException expected) {
}
}
@@ -357,6 +359,8 @@ public void testUnescapingInvalidCharacters() throws IOException {
reader.nextString();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Malformed Unicode escape \\u000g at line 1 column 5 path $[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -368,7 +372,9 @@ public void testUnescapingTruncatedCharacters() throws IOException {
try {
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Unterminated escape sequence at line 1 column 5 path $[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -380,7 +386,9 @@ public void testUnescapingTruncatedSequence() throws IOException {
try {
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Unterminated escape sequence at line 1 column 4 path $[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -446,6 +454,7 @@ public void testStrictNonFiniteDoubles() throws IOException {
reader.nextDouble();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -458,6 +467,8 @@ public void testStrictQuotedNonFiniteDoubles() throws IOException {
reader.nextDouble();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("JSON forbids NaN and infinities: NaN at line 1 column 7 path $[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -494,6 +505,7 @@ public void testStrictNonFiniteDoublesWithSkipValue() throws IOException {
reader.skipValue();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -532,8 +544,8 @@ public void testLongs() throws IOException {
}
@Test
- @Ignore
- public void disabled_testNumberWithOctalPrefix() throws IOException {
+ @Ignore("JsonReader advances after exception for invalid number was thrown; to be decided if that is acceptable")
+ public void testNumberWithOctalPrefix() throws IOException {
String json = "[01]";
JsonReader reader = new JsonReader(reader(json));
reader.beginArray();
@@ -541,21 +553,25 @@ public void disabled_testNumberWithOctalPrefix() throws IOException {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
try {
reader.nextInt();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "TODO");
}
try {
reader.nextLong();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "TODO");
}
try {
reader.nextDouble();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "TODO");
}
assertThat(reader.nextString()).isEqualTo("01");
reader.endArray();
@@ -582,6 +598,7 @@ public void testPeekingUnquotedStringsPrefixedWithBooleans() throws IOException
reader.nextBoolean();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a boolean", "STRING", "line 1 column 2 path $[0]");
}
assertThat(reader.nextString()).isEqualTo("truey");
reader.endArray();
@@ -723,7 +740,7 @@ public void testNegativeZero() throws Exception {
*/
@Test
@Ignore
- public void disabled_testPeekLargerThanLongMaxValue() throws IOException {
+ public void testPeekLargerThanLongMaxValue() throws IOException {
JsonReader reader = new JsonReader(reader("[9223372036854775808]"));
reader.setLenient(true);
reader.beginArray();
@@ -741,7 +758,7 @@ public void disabled_testPeekLargerThanLongMaxValue() throws IOException {
*/
@Test
@Ignore
- public void disabled_testPeekLargerThanLongMinValue() throws IOException {
+ public void testPeekLargerThanLongMinValue() throws IOException {
@SuppressWarnings("FloatingPointLiteralPrecision")
double d = -9223372036854775809d;
JsonReader reader = new JsonReader(reader("[-9223372036854775809]"));
@@ -762,7 +779,7 @@ public void disabled_testPeekLargerThanLongMinValue() throws IOException {
*/
@Test
@Ignore
- public void disabled_testHighPrecisionLong() throws IOException {
+ public void testHighPrecisionLong() throws IOException {
String json = "[9223372036854775806.000]";
JsonReader reader = new JsonReader(reader(json));
reader.beginArray();
@@ -817,7 +834,9 @@ public void testMissingValue() throws IOException {
try {
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected value at line 1 column 6 path $.a"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -830,38 +849,41 @@ public void testPrematureEndOfInput() throws IOException {
try {
reader.nextName();
fail();
- } catch (IOException expected) {
+ } catch (EOFException expected) {
}
}
@Test
public void testPrematurelyClosed() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":[]}"));
+ reader.beginObject();
+ reader.close();
try {
- JsonReader reader = new JsonReader(reader("{\"a\":[]}"));
- reader.beginObject();
- reader.close();
reader.nextName();
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("JsonReader is closed");
}
+ reader = new JsonReader(reader("{\"a\":[]}"));
+ reader.close();
try {
- JsonReader reader = new JsonReader(reader("{\"a\":[]}"));
- reader.close();
reader.beginObject();
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("JsonReader is closed");
}
+ reader = new JsonReader(reader("{\"a\":true}"));
+ reader.beginObject();
+ String unused1 = reader.nextName();
+ JsonToken unused2 = reader.peek();
+ reader.close();
try {
- JsonReader reader = new JsonReader(reader("{\"a\":true}"));
- reader.beginObject();
- String unused1 = reader.nextName();
- JsonToken unused2 = reader.peek();
- reader.close();
reader.nextBoolean();
fail();
} catch (IllegalStateException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("JsonReader is closed");
}
}
@@ -873,53 +895,63 @@ public void testNextFailuresDoNotAdvance() throws IOException {
String unused = reader.nextString();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a string", "NAME", "line 1 column 3 path $.");
}
assertThat(reader.nextName()).isEqualTo("a");
try {
String unused = reader.nextName();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a name", "BOOLEAN", "line 1 column 10 path $.a");
}
try {
reader.beginArray();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "BEGIN_ARRAY", "BOOLEAN", "line 1 column 10 path $.a");
}
try {
reader.endArray();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "END_ARRAY", "BOOLEAN", "line 1 column 10 path $.a");
}
try {
reader.beginObject();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "BEGIN_OBJECT", "BOOLEAN", "line 1 column 10 path $.a");
}
try {
reader.endObject();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "END_OBJECT", "BOOLEAN", "line 1 column 10 path $.a");
}
assertThat(reader.nextBoolean()).isTrue();
try {
reader.nextString();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a string", "END_OBJECT", "line 1 column 11 path $.a");
}
try {
reader.nextName();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a name", "END_OBJECT", "line 1 column 11 path $.a");
}
try {
reader.beginArray();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "BEGIN_ARRAY", "END_OBJECT", "line 1 column 11 path $.a");
}
try {
reader.endArray();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "END_ARRAY", "END_OBJECT", "line 1 column 11 path $.a");
}
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonToken.END_DOCUMENT);
@@ -947,6 +979,7 @@ public void testStringNullIsNotNull() throws IOException {
reader.nextNull();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "null", "STRING", "line 1 column 3 path $[0]");
}
}
@@ -958,6 +991,7 @@ public void testNullLiteralIsNotAString() throws IOException {
reader.nextString();
fail();
} catch (IllegalStateException expected) {
+ assertUnexpectedStructureError(expected, "a string", "NULL", "line 1 column 6 path $[0]");
}
}
@@ -969,7 +1003,8 @@ public void testStrictNameValueSeparator() throws IOException {
try {
reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
reader = new JsonReader(reader("{\"a\"=>true}"));
@@ -978,7 +1013,8 @@ public void testStrictNameValueSeparator() throws IOException {
try {
reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@@ -1005,7 +1041,8 @@ public void testStrictNameValueSeparatorWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
reader = new JsonReader(reader("{\"a\"=>true}"));
@@ -1014,7 +1051,8 @@ public void testStrictNameValueSeparatorWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@@ -1045,7 +1083,8 @@ public void testStrictComments() throws IOException {
try {
reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[# comment \n true]"));
@@ -1053,7 +1092,8 @@ public void testStrictComments() throws IOException {
try {
reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[/* comment */ true]"));
@@ -1061,7 +1101,8 @@ public void testStrictComments() throws IOException {
try {
reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1090,7 +1131,8 @@ public void testStrictCommentsWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[# comment \n true]"));
@@ -1098,7 +1140,8 @@ public void testStrictCommentsWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[/* comment */ true]"));
@@ -1106,7 +1149,8 @@ public void testStrictCommentsWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1117,7 +1161,8 @@ public void testStrictUnquotedNames() throws IOException {
try {
reader.nextName();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $.");
}
}
@@ -1136,7 +1181,8 @@ public void testStrictUnquotedNamesWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $.");
}
}
@@ -1147,7 +1193,8 @@ public void testStrictSingleQuotedNames() throws IOException {
try {
reader.nextName();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $.");
}
}
@@ -1166,7 +1213,8 @@ public void testStrictSingleQuotedNamesWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $.");
}
}
@@ -1178,6 +1226,7 @@ public void testStrictUnquotedStrings() throws IOException {
reader.nextString();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1189,6 +1238,7 @@ public void testStrictUnquotedStringsWithSkipValue() throws IOException {
reader.skipValue();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1207,7 +1257,8 @@ public void testStrictSingleQuotedStrings() throws IOException {
try {
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1226,7 +1277,8 @@ public void testStrictSingleQuotedStringsWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1235,10 +1287,10 @@ public void testStrictSemicolonDelimitedArray() throws IOException {
JsonReader reader = new JsonReader(reader("[true;true]"));
reader.beginArray();
try {
- boolean unused1 = reader.nextBoolean();
- boolean unused2 = reader.nextBoolean();
+ boolean unused = reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1256,10 +1308,10 @@ public void testStrictSemicolonDelimitedArrayWithSkipValue() throws IOException
JsonReader reader = new JsonReader(reader("[true;true]"));
reader.beginArray();
try {
- reader.skipValue();
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1269,10 +1321,10 @@ public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
try {
- boolean unused1 = reader.nextBoolean();
- String unused2 = reader.nextName();
+ boolean unused = reader.nextBoolean();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@@ -1292,10 +1344,10 @@ public void testStrictSemicolonDelimitedNameValuePairWithSkipValue() throws IOEx
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
try {
- reader.skipValue();
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@@ -1307,7 +1359,8 @@ public void testStrictUnnecessaryArraySeparators() throws IOException {
try {
reader.nextNull();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 8 path $[1]");
}
reader = new JsonReader(reader("[,true]"));
@@ -1315,7 +1368,8 @@ public void testStrictUnnecessaryArraySeparators() throws IOException {
try {
reader.nextNull();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[true,]"));
@@ -1324,7 +1378,8 @@ public void testStrictUnnecessaryArraySeparators() throws IOException {
try {
reader.nextNull();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 8 path $[1]");
}
reader = new JsonReader(reader("[,]"));
@@ -1332,7 +1387,8 @@ public void testStrictUnnecessaryArraySeparators() throws IOException {
try {
reader.nextNull();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1376,7 +1432,8 @@ public void testStrictUnnecessaryArraySeparatorsWithSkipValue() throws IOExcepti
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 8 path $[1]");
}
reader = new JsonReader(reader("[,true]"));
@@ -1384,7 +1441,8 @@ public void testStrictUnnecessaryArraySeparatorsWithSkipValue() throws IOExcepti
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
reader = new JsonReader(reader("[true,]"));
@@ -1393,7 +1451,8 @@ public void testStrictUnnecessaryArraySeparatorsWithSkipValue() throws IOExcepti
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 8 path $[1]");
}
reader = new JsonReader(reader("[,]"));
@@ -1401,7 +1460,8 @@ public void testStrictUnnecessaryArraySeparatorsWithSkipValue() throws IOExcepti
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@@ -1413,7 +1473,8 @@ public void testStrictMultipleTopLevelValues() throws IOException {
try {
reader.peek();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 5 path $");
}
}
@@ -1437,7 +1498,8 @@ public void testStrictMultipleTopLevelValuesWithSkipValue() throws IOException {
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 5 path $");
}
}
@@ -1477,22 +1539,24 @@ public void testTopLevelValueTypeWithSkipValue() throws IOException {
}
@Test
- public void testStrictNonExecutePrefix() {
+ public void testStrictNonExecutePrefix() throws IOException {
JsonReader reader = new JsonReader(reader(")]}'\n []"));
try {
reader.beginArray();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 1 path $");
}
}
@Test
- public void testStrictNonExecutePrefixWithSkipValue() {
+ public void testStrictNonExecutePrefixWithSkipValue() throws IOException {
JsonReader reader = new JsonReader(reader(")]}'\n []"));
try {
reader.skipValue();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 1 path $");
}
}
@@ -1515,14 +1579,16 @@ public void testLenientNonExecutePrefixWithLeadingWhitespace() throws IOExceptio
}
@Test
- public void testLenientPartialNonExecutePrefix() {
+ public void testLenientPartialNonExecutePrefix() throws IOException {
JsonReader reader = new JsonReader(reader(")]}' []"));
reader.setLenient(true);
+ assertThat(reader.nextString()).isEqualTo(")");
try {
- assertThat(reader.nextString()).isEqualTo(")");
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Unexpected value at line 1 column 3 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1540,7 +1606,8 @@ public void testBomForbiddenAsOtherCharacterInDocument() throws IOException {
try {
reader.endArray();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1606,8 +1673,8 @@ private void testFailWithPosition(String message, String json) throws IOExceptio
try {
JsonToken unused2 = reader1.peek();
fail();
- } catch (IOException expected) {
- assertThat(expected.getMessage()).isEqualTo(message);
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo(message + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
// Also validate that it works when skipping.
@@ -1618,8 +1685,8 @@ private void testFailWithPosition(String message, String json) throws IOExceptio
try {
JsonToken unused3 = reader2.peek();
fail();
- } catch (IOException expected) {
- assertThat(expected.getMessage()).isEqualTo(message);
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo(message + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1636,8 +1703,10 @@ public void testFailWithPositionDeepPath() throws IOException {
try {
JsonToken unused5 = reader.peek();
fail();
- } catch (IOException expected) {
- assertThat(expected.getMessage()).isEqualTo("Expected value at line 1 column 14 path $[1].a[2]");
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo(
+ "Expected value at line 1 column 14 path $[1].a[2]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1646,9 +1715,10 @@ public void testStrictVeryLongNumber() throws IOException {
JsonReader reader = new JsonReader(reader("[0." + repeat('9', 8192) + "]"));
reader.beginArray();
try {
- assertThat(reader.nextDouble()).isEqualTo(1d);
+ reader.nextDouble();
fail();
} catch (MalformedJsonException expected) {
+ assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@@ -1721,6 +1791,8 @@ public void testStringEndingInSlash() throws IOException {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected value at line 1 column 1 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1732,6 +1804,8 @@ public void testDocumentWithCommentEndingInSlash() throws IOException {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected value at line 1 column 10 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1743,6 +1817,8 @@ public void testStringWithLeadingSlash() throws IOException {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected value at line 1 column 1 path $"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1757,6 +1833,8 @@ public void testUnterminatedObject() throws IOException {
reader.peek();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Unterminated object at line 1 column 16 path $.a"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1883,7 +1961,9 @@ public void testStrictExtraCommasInMaps() throws IOException {
try {
reader.peek();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected name at line 1 column 11 path $.a"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1897,7 +1977,9 @@ public void testLenientExtraCommasInMaps() throws IOException {
try {
reader.peek();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Expected name at line 1 column 11 path $.a"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1909,45 +1991,45 @@ private String repeat(char c, int count) {
@Test
public void testMalformedDocuments() throws IOException {
- assertDocument("{]", BEGIN_OBJECT, IOException.class);
- assertDocument("{,", BEGIN_OBJECT, IOException.class);
- assertDocument("{{", BEGIN_OBJECT, IOException.class);
- assertDocument("{[", BEGIN_OBJECT, IOException.class);
- assertDocument("{:", BEGIN_OBJECT, IOException.class);
- assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\":}", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\"::", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\":,", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\"=}", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\"=>}", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\"=>\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{]", BEGIN_OBJECT, MalformedJsonException.class);
+ assertDocument("{,", BEGIN_OBJECT, MalformedJsonException.class);
+ assertDocument("{{", BEGIN_OBJECT, MalformedJsonException.class);
+ assertDocument("{[", BEGIN_OBJECT, MalformedJsonException.class);
+ assertDocument("{:", BEGIN_OBJECT, MalformedJsonException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\":}", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\"::", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\":,", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\"=}", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\"=>}", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING, MalformedJsonException.class);
+ assertDocument("{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING, MalformedJsonException.class);
+ assertDocument("{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING, MalformedJsonException.class);
+ assertDocument("{\"name\"=>\"string\",", BEGIN_OBJECT, NAME, STRING, EOFException.class);
assertDocument("{\"name\"=>\"string\",\"name\"", BEGIN_OBJECT, NAME, STRING, NAME);
- assertDocument("[}", BEGIN_ARRAY, IOException.class);
+ assertDocument("[}", BEGIN_ARRAY, MalformedJsonException.class);
assertDocument("[,]", BEGIN_ARRAY, NULL, NULL, END_ARRAY);
- assertDocument("{", BEGIN_OBJECT, IOException.class);
- assertDocument("{\"name\"", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{'name'", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{'name',", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("{name", BEGIN_OBJECT, NAME, IOException.class);
- assertDocument("[", BEGIN_ARRAY, IOException.class);
- assertDocument("[string", BEGIN_ARRAY, STRING, IOException.class);
- assertDocument("[\"string\"", BEGIN_ARRAY, STRING, IOException.class);
- assertDocument("['string'", BEGIN_ARRAY, STRING, IOException.class);
- assertDocument("[123", BEGIN_ARRAY, NUMBER, IOException.class);
- assertDocument("[123,", BEGIN_ARRAY, NUMBER, IOException.class);
- assertDocument("{\"name\":123", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
- assertDocument("{\"name\":123,", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
- assertDocument("{\"name\":\"string\"", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\":\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\":'string'", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\":'string',", BEGIN_OBJECT, NAME, STRING, IOException.class);
- assertDocument("{\"name\":false", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
- assertDocument("{\"name\":false,,", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
+ assertDocument("{", BEGIN_OBJECT, EOFException.class);
+ assertDocument("{\"name\"", BEGIN_OBJECT, NAME, EOFException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{'name'", BEGIN_OBJECT, NAME, EOFException.class);
+ assertDocument("{'name',", BEGIN_OBJECT, NAME, MalformedJsonException.class);
+ assertDocument("{name", BEGIN_OBJECT, NAME, EOFException.class);
+ assertDocument("[", BEGIN_ARRAY, EOFException.class);
+ assertDocument("[string", BEGIN_ARRAY, STRING, EOFException.class);
+ assertDocument("[\"string\"", BEGIN_ARRAY, STRING, EOFException.class);
+ assertDocument("['string'", BEGIN_ARRAY, STRING, EOFException.class);
+ assertDocument("[123", BEGIN_ARRAY, NUMBER, EOFException.class);
+ assertDocument("[123,", BEGIN_ARRAY, NUMBER, EOFException.class);
+ assertDocument("{\"name\":123", BEGIN_OBJECT, NAME, NUMBER, EOFException.class);
+ assertDocument("{\"name\":123,", BEGIN_OBJECT, NAME, NUMBER, EOFException.class);
+ assertDocument("{\"name\":\"string\"", BEGIN_OBJECT, NAME, STRING, EOFException.class);
+ assertDocument("{\"name\":\"string\",", BEGIN_OBJECT, NAME, STRING, EOFException.class);
+ assertDocument("{\"name\":'string'", BEGIN_OBJECT, NAME, STRING, EOFException.class);
+ assertDocument("{\"name\":'string',", BEGIN_OBJECT, NAME, STRING, EOFException.class);
+ assertDocument("{\"name\":false", BEGIN_OBJECT, NAME, BOOLEAN, EOFException.class);
+ assertDocument("{\"name\":false,,", BEGIN_OBJECT, NAME, BOOLEAN, MalformedJsonException.class);
}
/**
@@ -1964,6 +2046,8 @@ public void testUnterminatedStringFailure() throws IOException {
reader.nextString();
fail();
} catch (MalformedJsonException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("Unterminated string at line 1 column 9 path $[0]"
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
}
}
@@ -1983,6 +2067,17 @@ public void testReadAcrossBuffers() throws IOException {
assertThat(token).isEqualTo(JsonToken.NUMBER);
}
+ private static void assertStrictError(MalformedJsonException exception, String expectedLocation) {
+ assertThat(exception).hasMessageThat().isEqualTo("Use JsonReader.setLenient(true) to accept malformed JSON at " + expectedLocation
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#malformed-json");
+ }
+
+ private static void assertUnexpectedStructureError(IllegalStateException exception, String expectedToken, String actualToken, String expectedLocation) {
+ String troubleshootingId = actualToken.equals("NULL") ? "adapter-not-null-safe" : "unexpected-json-structure";
+ assertThat(exception).hasMessageThat().isEqualTo("Expected " + expectedToken + " but was " + actualToken + " at " + expectedLocation
+ + "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#" + troubleshootingId);
+ }
+
private void assertDocument(String document, Object... expectations) throws IOException {
JsonReader reader = new JsonReader(reader(document));
reader.setLenient(true);
@@ -2005,15 +2100,15 @@ private void assertDocument(String document, Object... expectations) throws IOEx
assertThat(reader.nextInt()).isEqualTo(123);
} else if (expectation == NULL) {
reader.nextNull();
- } else if (expectation == IOException.class) {
+ } else if (expectation instanceof Class && Exception.class.isAssignableFrom((Class>) expectation)) {
try {
reader.peek();
fail();
- } catch (IOException expected) {
- // OK: Should fail
+ } catch (Exception expected) {
+ assertThat(expected.getClass()).isEqualTo((Class>) expectation);
}
} else {
- throw new AssertionError();
+ throw new AssertionError("Unsupported expectation value: " + expectation);
}
}
}
diff --git a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java
index 70470a166b..2ee120f38e 100644
--- a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java
+++ b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java
@@ -472,7 +472,7 @@ public void testMalformedNumbers() throws IOException {
jsonWriter.value(new LazilyParsedNumber(malformedNumber));
fail("Should have failed writing malformed number: " + malformedNumber);
} catch (IllegalArgumentException e) {
- assertThat(e.getMessage()).isEqualTo("String created by class com.google.gson.internal.LazilyParsedNumber is not a valid JSON number: " + malformedNumber);
+ assertThat(e).hasMessageThat().isEqualTo("String created by class com.google.gson.internal.LazilyParsedNumber is not a valid JSON number: " + malformedNumber);
}
}
}