Link to troubleshooting guide from exception messages (#2357)

* Link to troubleshooting guide from exception messages

* Add examples to troubleshooting guide

* Use proper anchor names for troubleshooting guide
This commit is contained in:
Marcono1234 2023-04-15 22:36:26 +02:00 committed by GitHub
parent 1717fd62cf
commit cbad1aa79f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 517 additions and 248 deletions

View File

@ -2,7 +2,10 @@
This guide describes how to troubleshoot common issues when using Gson.
## `ClassCastException` when using deserialized object
<!-- The '<a id="..."></a>' anchors below are used to create stable links; don't remove or rename them -->
<!-- Use only lowercase IDs, GitHub seems to not support uppercase IDs, see also https://github.com/orgs/community/discussions/50962 -->
## <a id="class-cast-exception"></a> `ClassCastException` when using deserialized object
**Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson
@ -16,7 +19,7 @@ This guide describes how to troubleshoot common issues when using Gson.
The overloads with `Type` parameter do not provide any type-safety guarantees.
- When using `TypeToken` make sure you don't capture a type variable. For example avoid something like `new TypeToken<List<T>>()` (where `T` is a type variable). Due to Java type erasure the actual type of `T` is not available at runtime. Refactor your code to pass around `TypeToken` instances or use [`TypeToken.getParameterized(...)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/reflect/TypeToken.html#getParameterized(java.lang.reflect.Type,java.lang.reflect.Type...)), for example `TypeToken.getParameterized(List.class, elementClass)`.
## `InaccessibleObjectException`: 'module ... does not "opens ..." to unnamed module'
## <a id="reflection-inaccessible"></a> `InaccessibleObjectException`: 'module ... does not "opens ..." to unnamed module'
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to unnamed module' is thrown
@ -30,7 +33,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`.
## `InaccessibleObjectException`: 'module ... does not "opens ..." to module com.google.gson'
## <a id="reflection-inaccessible-to-module-gson"></a> `InaccessibleObjectException`: 'module ... does not "opens ..." to module com.google.gson'
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to module com.google.gson' is thrown
@ -51,7 +54,7 @@ module mymodule {
Or in case this occurs for a field in one of your classes which you did not actually want to serialize or deserialize in the first place, you can exclude that field, see the [user guide](UserGuide.md#excluding-fields-from-serialization-and-deserialization).
## Android app not working in Release mode; random property names
## <a id="android-app-random-names"></a> Android app not working in Release mode; random property names
**Symptom:** Your Android app is working fine in Debug mode but fails in Release mode and the JSON properties have seemingly random names such as `a`, `b`, ...
@ -59,7 +62,7 @@ Or in case this occurs for a field in one of your classes which you did not actu
**Solution:** Make sure you have configured ProGuard / R8 correctly to preserve the names of your fields. See the [Android example](examples/android-proguard-example/README.md) for more information.
## Android app unable to parse JSON after app update
## <a id="android-app-broken-after-app-update"></a> Android app unable to parse JSON after app update
**Symptom:** You released a new version of your Android app and it fails to parse JSON data created by the previous version of your app
@ -71,7 +74,7 @@ If you want to preserve backward compatibility for you app you can use [`@Serial
Normally ProGuard and R8 produce a mapping file, this makes it easier to find out the obfuscated field names instead of having to find them out through trial and error or other means. See the [Android Studio user guide](https://developer.android.com/studio/build/shrink-code.html#retracing) for more information.
## Default field values not present after deserialization
## <a id="default-field-values-missing"></a> Default field values not present after deserialization
**Symptom:** You have assign default values to fields but after deserialization the fields have their standard value (such as `null` or `0`)
@ -84,7 +87,7 @@ Normally ProGuard and R8 produce a mapping file, this makes it easier to find ou
Otherwise Gson will by default try to use JDK `Unsafe` or similar means to create an instance of your class without invoking the constructor and without running any initializers. You can also disable that behavior through [`GsonBuilder.disableJdkUnsafe()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()) to notice such issues early on.
## `null` values for anonymous and local classes
## <a id="anonymous-local-null"></a> `null` values for anonymous and local classes
**Symptom:** Objects of a class are always serialized as JSON `null` / always deserialized as Java `null`
@ -97,7 +100,7 @@ Notes:
- "double brace-initialization" also creates anonymous classes
- Local record classes (feature added in Java 16) are supported by Gson and are not affected by this
## Map keys having unexpected format in JSON
## <a id="map-key-wrong-json"></a> Map keys having unexpected format in JSON
**Symptom:** JSON output for `Map` keys is unexpected / cannot be deserialized again
@ -105,15 +108,32 @@ Notes:
**Solution:** Use [`GsonBuilder.enableComplexMapKeySerialization()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#enableComplexMapKeySerialization()). See also the [user guide](UserGuide.md#maps-examples) for more information.
## Parsing JSON fails with `MalformedJsonException`
## <a id="malformed-json"></a> Parsing JSON fails with `MalformedJsonException`
**Symptom:** JSON parsing fails with `MalformedJsonException`
**Reason:** The JSON data is actually malformed
**Solution:** During debugging log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Sometimes APIs might return HTML error pages (instead of JSON data) when reaching rate limits or when other errors occur. Also read the location information of the `MalformedJsonException` exception message, it indicates where exactly in the document the malformed data was detected, including the [JSONPath](https://goessner.net/articles/JsonPath/).
**Solution:** During debugging, log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Sometimes APIs might return HTML error pages (instead of JSON data) when reaching rate limits or when other errors occur. Also read the location information of the `MalformedJsonException` exception message, it indicates where exactly in the document the malformed data was detected, including the [JSONPath](https://goessner.net/articles/JsonPath/).
## Integral JSON number is parsed as `double`
For example, let's assume you want to deserialize the following JSON data:
```json
{
"languages": [
"English",
"French",
]
}
```
This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 5 column 4 path $.languages[2]`
The problem here is the trailing comma (`,`) after `"French"`, trailing commas are not allowed by the JSON specification. The location information "line 5 column 4" points to the `]` in the JSON data (with some slight inaccuracies) because Gson expected another value after `,` instead of the closing `]`. The JSONPath `$.languages[2]` in the exception message also points there: `$.` refers to the root object, `languages` refers to its member of that name and `[2]` refers to the (missing) third value in the JSON array value of that member (numbering starts at 0, so it is `[2]` instead of `[3]`).
The proper solution here is to fix the malformed JSON data.
To spot syntax errors in the JSON data easily you can open it in an editor with support for JSON, for example Visual Studio Code. It will highlight within the JSON data the error location and show why the JSON data is considered invalid.
## <a id="number-parsed-as-double"></a> Integral JSON number is parsed as `double`
**Symptom:** JSON data contains an integral number such as `45` but Gson returns it as `double`
@ -121,17 +141,17 @@ Notes:
**Solution:** Use [`GsonBuilder.setObjectToNumberStrategy`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#setObjectToNumberStrategy(com.google.gson.ToNumberStrategy)) to specify what type of number should be returned
## Malformed JSON not rejected
## <a id="default-lenient"></a> Malformed JSON not rejected
**Symptom:** Gson parses malformed JSON without throwing any exceptions
**Reason:** Due to legacy reasons Gson performs parsing by default in lenient mode
**Solution:** See [`Gson` class documentation](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html) section "Lenient JSON handling"
**Solution:** See [`Gson` class documentation](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html#default-lenient) section "Lenient JSON handling"
Note: Even in non-lenient mode Gson deviates slightly from the JSON specification, see [`JsonReader.setLenient`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonReader.html#setLenient(boolean)) for more details.
## `IllegalStateException`: "Expected ... but was ..."
## <a id="unexpected-json-structure"></a> `IllegalStateException`: "Expected ... but was ..."
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was ..." is thrown
@ -139,13 +159,36 @@ Note: Even in non-lenient mode Gson deviates slightly from the JSON specificatio
**Solution:** Make sure that your classes correctly model the JSON data. Also during debugging log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Read the location information of the exception message, it indicates where exactly in the document the error occurred, including the [JSONPath](https://goessner.net/articles/JsonPath/).
## `IllegalStateException`: "Expected ... but was NULL"
For example, let's assume you have the following Java class:
```java
class WebPage {
String languages;
}
```
And you want to deserialize the following JSON data:
```json
{
"languages": ["English", "French"]
}
```
This will fail with an exception similar to this one: `IllegalStateException: Expected a string but was BEGIN_ARRAY at line 2 column 17 path $.languages`
This means Gson expected a JSON string value but found the beginning of a JSON array (`[`). The location information "line 2 column 17" points to the `[` in the JSON data (with some slight inaccuracies), so does the JSONPath `$.languages` in the exception message. It refers to the `languages` member of the root object (`$.`).
The solution here is to change in the `WebPage` class the field `String languages` to `List<String> languages`.
## <a id="adapter-not-null-safe"></a> `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
## <a id="serialize-nulls"></a> 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
## <a id="android-internal-fields"></a> 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
## <a id="json-static-fields"></a> 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
## <a id="no-such-method-error"></a> `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.
## <a id="duplicate-fields"></a> `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.
## <a id="java-lang-class-unsupported"></a> `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`.

View File

@ -105,7 +105,7 @@ import java.util.concurrent.atomic.AtomicLongArray;
* <p>See the <a href="https://github.com/google/gson/blob/master/UserGuide.md">Gson User Guide</a>
* for a more complete set of examples.</p>
*
* <h2>Lenient JSON handling</h2>
* <h2 id="default-lenient">Lenient JSON handling</h2>
* 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:

View File

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

View File

@ -33,6 +33,7 @@ import com.google.gson.internal.Excluder;
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 final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
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 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
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()));

View File

@ -28,6 +28,7 @@ import com.google.gson.TypeAdapter;
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 @@ public final class 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();

View File

@ -18,6 +18,7 @@ package com.google.gson.internal.reflect;
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 class ReflectionHelper {
} 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 class ReflectionHelper {
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);
}
}

View File

@ -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 class JsonReader implements Closeable {
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 class JsonReader implements Closeable {
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 class JsonReader implements Closeable {
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 class JsonReader implements Closeable {
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 class JsonReader implements Closeable {
} 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 class JsonReader implements Closeable {
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 class JsonReader implements Closeable {
pathIndices[stackSize - 1]++;
return false;
}
throw new IllegalStateException("Expected a boolean but was " + peek() + locationString());
throw unexpectedTokenError("a boolean");
}
/**
@ -880,7 +881,7 @@ public class JsonReader implements Closeable {
peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++;
} else {
throw new IllegalStateException("Expected null but was " + peek() + locationString());
throw unexpectedTokenError("null");
}
}
@ -915,14 +916,13 @@ public class JsonReader implements Closeable {
} 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 class JsonReader implements Closeable {
// 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 class JsonReader implements Closeable {
// 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 class JsonReader implements Closeable {
/**
* 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 @@ public class JsonReader implements Closeable {
} 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 @@ public class JsonReader implements Closeable {
* 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 @@ public class JsonReader implements Closeable {
} 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");
}
}
};

View File

@ -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 final class JsonArrayTest {
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 final class JsonArrayTest {
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\"");
}
}

View File

@ -37,7 +37,9 @@ public class ToNumberPolicyTest {
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 class ToNumberPolicyTest {
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 class ToNumberPolicyTest {
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");
}
}

View File

@ -95,6 +95,8 @@ public class DefaultTypeAdaptersTest {
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 class DefaultTypeAdaptersTest {
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 class DefaultTypeAdaptersTest {
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 class DefaultTypeAdaptersTest {
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 $");
}
}

View File

@ -53,8 +53,8 @@ public class GsonVersionDiagnosticsTest {
@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 @@ public class GsonVersionDiagnosticsTest {
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 {

View File

@ -59,7 +59,7 @@ public class MapAsArrayTypeAdapterTest {
@Test
@Ignore
public void disabled_testTwoTypesCollapseToOneSerialize() {
public void testTwoTypesCollapseToOneSerialize() {
Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();

View File

@ -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 class MoreSpecificTypeSerializationTest {
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 class MoreSpecificTypeSerializationTest {
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\":");
}
/**

View File

@ -26,6 +26,7 @@ import com.google.gson.annotations.SerializedName;
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 class NamingPolicyTest {
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 @@ public class NamingPolicyTest {
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 @@ public class NamingPolicyTest {
this.value = value;
}
}
@SuppressWarnings("unused")
private static class ClassWithTwoFields {
public int a;
public int b;
public ClassWithTwoFields() {}
}
}

View File

@ -177,7 +177,8 @@ public class ObjectTest {
} 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 class ObjectTest {
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 class ObjectTest {
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 class ObjectTest {
}
// 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);
}
}

View File

@ -92,21 +92,21 @@ public class PrimitiveTest {
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 class PrimitiveTest {
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 class PrimitiveTest {
@Test
public void testStringsAsBooleans() {
String json = "['true', 'false', 'TRUE', 'yes', '1']";
assertThat( gson.<List<Boolean>>fromJson(json, new TypeToken<List<Boolean>>() {}.getType())).isEqualTo(Arrays.asList(true, false, true, false, false));
List<Boolean> deserialized = gson.fromJson(json, new TypeToken<List<Boolean>>() {});
assertThat(deserialized).isEqualTo(Arrays.asList(true, false, true, false, false));
}
}

View File

@ -79,7 +79,7 @@ public class ReflectionAccessTest {
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 class ReflectionAccessTest {
}
}
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 class ReflectionAccessTest {
// 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.");
}
}

View File

@ -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 final class StreamingTypeAdaptersTest {
public void testDeserialize1dArray() throws IOException {
TypeAdapter<double[]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[]>() {});
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 final class StreamingTypeAdaptersTest {
TypeAdapter<double[][]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[][]>() {});
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 final class StreamingTypeAdaptersTest {
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 final class StreamingTypeAdaptersTest {
+ "'left':{'label':'left','left':null,'right':null},"
+ "'right':{'label':'right','left':null,'right':null}}");
}
@Test
public void testFromJsonTree() {
JsonObject truckObject = new JsonObject();

View File

@ -75,7 +75,7 @@ public final class TypeAdapterPrecedenceTest {
.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

View File

@ -99,7 +99,7 @@ public final class LinkedTreeMapTest {
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 final class LinkedTreeMapTest {
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");

View File

@ -63,21 +63,21 @@ public final class JsonElementReaderTest {
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();

View File

@ -133,7 +133,7 @@ public class JsonTreeReaderTest {
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");
}
}

View File

@ -34,7 +34,7 @@ import org.junit.Test;
/**
* 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 class PerformanceTest {
@Test
@Ignore
public void disabled_testStringDeserialization() {
public void testStringDeserialization() {
StringBuilder sb = new StringBuilder(8096);
sb.append("Error Yippie");
@ -117,7 +117,7 @@ public class PerformanceTest {
*/
@Test
@Ignore
public void disabled_testLargeCollectionSerialization() {
public void testLargeCollectionSerialization() {
int count = 1400000;
List<CollectionEntry> list = new ArrayList<>(count);
for (int i = 0; i < count; ++i) {
@ -131,7 +131,7 @@ public class PerformanceTest {
*/
@Test
@Ignore
public void disabled_testLargeCollectionDeserialization() {
public void testLargeCollectionDeserialization() {
StringBuilder sb = new StringBuilder();
int count = 87000;
boolean first = true;
@ -157,7 +157,7 @@ public class PerformanceTest {
// 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 class PerformanceTest {
// 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 class PerformanceTest {
@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 class PerformanceTest {
@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 class PerformanceTest {
@Test
@Ignore
public void disabled_testLargeObjectSerializationAndDeserialization() {
public void testLargeObjectSerializationAndDeserialization() {
Map<String, Long> largeObject = new HashMap<>();
for (long l = 0; l < 100000; l++) {
largeObject.put("field" + l, l);
@ -256,7 +256,7 @@ public class PerformanceTest {
@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 class PerformanceTest {
@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 class PerformanceTest {
@Test
@Ignore
public void disabled_testLargeGsonMapRoundTrip() throws Exception {
public void testLargeGsonMapRoundTrip() throws Exception {
Map<Long, Long> original = new HashMap<>();
for (long i = 0; i < 1000000; i++) {
original.put(i, i + 1);

View File

@ -235,21 +235,21 @@ public final class TypeTokenTest {
new SubTypeToken<Integer>() {};
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<Integer>();
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");
}
}

View File

@ -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.NAME;
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 final class JsonReaderTest {
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 final class JsonReaderTest {
}
@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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
reader.nextDouble();
fail();
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@ -458,6 +467,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
reader.skipValue();
fail();
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@ -532,8 +544,8 @@ public final class JsonReaderTest {
}
@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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
*/
@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 final class JsonReaderTest {
*/
@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 final class JsonReaderTest {
*/
@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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
reader.nextNull();
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(expected, "null", "STRING", "line 1 column 3 path $[0]");
}
}
@ -958,6 +991,7 @@ public final class JsonReaderTest {
reader.nextString();
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(expected, "a string", "NULL", "line 1 column 6 path $[0]");
}
}
@ -969,7 +1003,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.nextBoolean();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@ -1005,7 +1041,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 6 path $.a");
}
}
@ -1045,7 +1083,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.nextBoolean();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1090,7 +1131,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1117,7 +1161,8 @@ public final class JsonReaderTest {
try {
reader.nextName();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $.");
}
}
@ -1136,7 +1181,8 @@ public final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $.");
}
}
@ -1147,7 +1193,8 @@ public final class JsonReaderTest {
try {
reader.nextName();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $.");
}
}
@ -1166,7 +1213,8 @@ public final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $.");
}
}
@ -1178,6 +1226,7 @@ public final class JsonReaderTest {
reader.nextString();
fail();
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@ -1189,6 +1238,7 @@ public final class JsonReaderTest {
reader.skipValue();
fail();
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@ -1207,7 +1257,8 @@ public final class JsonReaderTest {
try {
reader.nextString();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1226,7 +1277,8 @@ public final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1235,10 +1287,10 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.nextNull();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1376,7 +1432,8 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 3 path $[0]");
}
}
@ -1413,7 +1473,8 @@ public final class JsonReaderTest {
try {
reader.peek();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 5 path $");
}
}
@ -1437,7 +1498,8 @@ public final class JsonReaderTest {
try {
reader.skipValue();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 5 path $");
}
}
@ -1477,22 +1539,24 @@ public final class JsonReaderTest {
}
@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 final class JsonReaderTest {
}
@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 final class JsonReaderTest {
try {
reader.endArray();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
assertStrictError(expected, "line 1 column 2 path $[0]");
}
}
@ -1606,8 +1673,8 @@ public final class JsonReaderTest {
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 @@ public final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 @@ public final class JsonReaderTest {
@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 final class JsonReaderTest {
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 final class JsonReaderTest {
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 @@ public final class JsonReaderTest {
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);
}
}
}

View File

@ -472,7 +472,7 @@ public final class JsonWriterTest {
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);
}
}
}