Updates the line separator from CRLF to LF (#2536)

This commit is contained in:
Maicol 2023-11-10 21:36:42 +01:00 committed by GitHub
parent ad7b6a85d6
commit c2a0e4634a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1582 additions and 1582 deletions

View File

@ -1,357 +1,357 @@
# Troubleshooting Guide # Troubleshooting Guide
This guide describes how to troubleshoot common issues when using Gson. This guide describes how to troubleshoot common issues when using Gson.
<!-- The '<a id="..."></a>' anchors below are used to create stable links; don't remove or rename them --> <!-- 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 --> <!-- 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 ## <a id="class-cast-exception"></a> `ClassCastException` when using deserialized object
**Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson **Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson
**Reason:** Your code is most likely not type-safe **Reason:** Your code is most likely not type-safe
**Solution:** Make sure your code adheres to the following: **Solution:** Make sure your code adheres to the following:
- Avoid raw types: Instead of calling `fromJson(..., List.class)`, create for example a `TypeToken<List<MyClass>>`. - Avoid raw types: Instead of calling `fromJson(..., List.class)`, create for example a `TypeToken<List<MyClass>>`.
See the [user guide](UserGuide.md#collections-examples) for more information. See the [user guide](UserGuide.md#collections-examples) for more information.
- When using `TypeToken` prefer the `Gson.fromJson` overloads with `TypeToken` parameter such as [`fromJson(Reader, TypeToken)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html#fromJson(java.io.Reader,com.google.gson.reflect.TypeToken)). - When using `TypeToken` prefer the `Gson.fromJson` overloads with `TypeToken` parameter such as [`fromJson(Reader, TypeToken)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html#fromJson(java.io.Reader,com.google.gson.reflect.TypeToken)).
The overloads with `Type` parameter do not provide any type-safety guarantees. 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](https://dev.java/learn/generics/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, elementType)` where `elementType` is a type you have to provide separately. - 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](https://dev.java/learn/generics/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, elementType)` where `elementType` is a type you have to provide separately.
## <a id="reflection-inaccessible"></a> `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 **Symptom:** An exception with a message in the form 'module ... does not "opens ..." to unnamed module' is thrown
**Reason:** You use Gson by accident to access internal fields of third-party classes **Reason:** You use Gson by accident to access internal fields of third-party classes
**Solution:** Write custom Gson [`TypeAdapter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) implementations for the affected classes or change the type of your data. If 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). **Solution:** Write custom Gson [`TypeAdapter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) implementations for the affected classes or change the type of your data. If 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).
**Explanation:** **Explanation:**
When no built-in adapter for a type exists and no custom adapter has been registered, Gson falls back to using reflection to access the fields of a class (including `private` ones). Most likely you are seeing this error because you (by accident) rely on the reflection-based adapter for third-party classes. That should be avoided because you make yourself dependent on the implementation details of these classes which could change at any point. For the JDK it is also not possible anymore to access internal fields using reflection starting with JDK 17, see [JEP 403](https://openjdk.org/jeps/403). When no built-in adapter for a type exists and no custom adapter has been registered, Gson falls back to using reflection to access the fields of a class (including `private` ones). Most likely you are seeing this error because you (by accident) rely on the reflection-based adapter for third-party classes. That should be avoided because you make yourself dependent on the implementation details of these classes which could change at any point. For the JDK it is also not possible anymore to access internal fields using reflection starting with JDK 17, see [JEP 403](https://openjdk.org/jeps/403).
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`. 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`.
## <a id="reflection-inaccessible-to-module-gson"></a> `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 **Symptom:** An exception with a message in the form 'module ... does not "opens ..." to module com.google.gson' is thrown
**Reason:** **Reason:**
- If the reported package is your own package then you have not configured the module declaration of your project to allow Gson to use reflection on your classes. - If the reported package is your own package then you have not configured the module declaration of your project to allow Gson to use reflection on your classes.
- If the reported package is from a third party library or the JDK see [this troubleshooting point](#inaccessibleobjectexception-module--does-not-opens--to-unnamed-module). - If the reported package is from a third party library or the JDK see [this troubleshooting point](#inaccessibleobjectexception-module--does-not-opens--to-unnamed-module).
**Solution:** Make sure the `module-info.java` file of your project allows Gson to use reflection on your classes, for example: **Solution:** Make sure the `module-info.java` file of your project allows Gson to use reflection on your classes, for example:
```java ```java
module mymodule { module mymodule {
requires com.google.gson; requires com.google.gson;
opens mypackage to com.google.gson; opens mypackage to com.google.gson;
} }
``` ```
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). 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).
## <a id="android-app-random-names"></a> 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`, ... **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`, ...
**Reason:** You probably have not configured ProGuard / R8 correctly **Reason:** You probably have not configured ProGuard / R8 correctly
**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. **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.
## <a id="android-app-broken-after-app-update"></a> 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 **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
**Reason:** You probably have not configured ProGuard / R8 correctly; probably the field names are being obfuscated and their naming changed between the versions of your app **Reason:** You probably have not configured ProGuard / R8 correctly; probably the field names are being obfuscated and their naming changed between the versions of your app
**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. **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.
If you want to preserve backward compatibility for you app you can use [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) on the fields to specify the obfuscated name as alternate, for example: `@SerializedName(value = "myprop", alternate = "a")` If you want to preserve backward compatibility for you app you can use [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) on the fields to specify the obfuscated name as alternate, for example: `@SerializedName(value = "myprop", alternate = "a")`
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. 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.
## <a id="default-field-values-missing"></a> 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`) **Symptom:** You have assign default values to fields but after deserialization the fields have their standard value (such as `null` or `0`)
**Reason:** Gson cannot invoke the constructor of your class and falls back to JDK `Unsafe` (or similar means) **Reason:** Gson cannot invoke the constructor of your class and falls back to JDK `Unsafe` (or similar means)
**Solution:** Make sure that the class: **Solution:** Make sure that the class:
- is `static` (explicitly or implicitly when it is a top-level class) - is `static` (explicitly or implicitly when it is a top-level class)
- has a no-args constructor - has a no-args constructor
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. 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.
## <a id="anonymous-local-null"></a> `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` **Symptom:** Objects of a class are always serialized as JSON `null` / always deserialized as Java `null`
**Reason:** The class you are serializing or deserializing is an anonymous or a local class (or you have specified a custom `ExclusionStrategy`) **Reason:** The class you are serializing or deserializing is an anonymous or a local class (or you have specified a custom `ExclusionStrategy`)
**Solution:** Convert the class to a `static` nested class. If the class is already `static` make sure you have not specified a Gson `ExclusionStrategy` which might exclude the class. **Solution:** Convert the class to a `static` nested class. If the class is already `static` make sure you have not specified a Gson `ExclusionStrategy` which might exclude the class.
Notes: Notes:
- "double brace-initialization" also creates anonymous classes - "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 - Local record classes (feature added in Java 16) are supported by Gson and are not affected by this
## <a id="map-key-wrong-json"></a> 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 **Symptom:** JSON output for `Map` keys is unexpected / cannot be deserialized again
**Reason:** The `Map` key type is 'complex' and you have not configured the `GsonBuilder` properly **Reason:** The `Map` key type is 'complex' and you have not configured the `GsonBuilder` properly
**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. **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.
## <a id="malformed-json"></a> Parsing JSON fails with `MalformedJsonException` ## <a id="malformed-json"></a> Parsing JSON fails with `MalformedJsonException`
**Symptom:** JSON parsing fails with `MalformedJsonException` **Symptom:** JSON parsing fails with `MalformedJsonException`
**Reason:** The JSON data is actually malformed **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/).
For example, let's assume you want to deserialize the following JSON data: For example, let's assume you want to deserialize the following JSON data:
```json ```json
{ {
"languages": [ "languages": [
"English", "English",
"French", "French",
] ]
} }
``` ```
This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON at line 5 column 4 path $.languages[2]` This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setStrictness(Strictness.LENIENT) 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 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. 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. 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` ## <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` **Symptom:** JSON data contains an integral number such as `45` but Gson returns it as `double`
**Reason:** When parsing a JSON number as `Object`, Gson will by default always return a `double` **Reason:** When parsing a JSON number as `Object`, Gson will by default always return a `double`
**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 **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
## <a id="default-lenient"></a> Malformed JSON not rejected ## <a id="default-lenient"></a> Malformed JSON not rejected
**Symptom:** Gson parses malformed JSON without throwing any exceptions **Symptom:** Gson parses malformed JSON without throwing any exceptions
**Reason:** Due to legacy reasons Gson performs parsing by default in lenient mode **Reason:** Due to legacy reasons Gson performs parsing by default in lenient mode
**Solution:** If you are using Gson 2.11.0 or newer, call [`GsonBuilder.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#setStrictness(com.google.gson.Strictness)), **Solution:** If you are using Gson 2.11.0 or newer, call [`GsonBuilder.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#setStrictness(com.google.gson.Strictness)),
[`JsonReader.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonReader.html#setStrictness(com.google.gson.Strictness)) [`JsonReader.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonReader.html#setStrictness(com.google.gson.Strictness))
and [`JsonWriter.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonWriter.html#setStrictness(com.google.gson.Strictness)) and [`JsonWriter.setStrictness`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonWriter.html#setStrictness(com.google.gson.Strictness))
with `Strictness.STRICT` to overwrite the default lenient behavior of `Gson` and make these classes strictly adhere to the JSON specification. with `Strictness.STRICT` to overwrite the default lenient behavior of `Gson` and make these classes strictly adhere to the JSON specification.
Otherwise if you are using an older Gson version, see the [`Gson` class documentation](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html#default-lenient) Otherwise if you are using an older Gson version, see the [`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 "JSON Strictness handling" for alternative solutions. section "JSON Strictness handling" for alternative solutions.
## <a id="unexpected-json-structure"></a> `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 **Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was ..." is thrown
**Reason:** The JSON data does not have the correct format **Reason:** The JSON data does not have the correct format
**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/). **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/).
For example, let's assume you have the following Java class: For example, let's assume you have the following Java class:
```java ```java
class WebPage { class WebPage {
String languages; String languages;
} }
``` ```
And you want to deserialize the following JSON data: And you want to deserialize the following JSON data:
```json ```json
{ {
"languages": ["English", "French"] "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 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 (`$.`). 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`. 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" ## <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 **Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was NULL" is thrown
**Reason:** **Reason:**
- A built-in adapter does not support JSON null values - A built-in adapter does not support JSON null values
- You have written a custom `TypeAdapter` which does not properly handle 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: **Solution:** If this occurs for a custom adapter you wrote, add code similar to the following at the beginning of its `read` method:
```java ```java
@Override @Override
public MyClass read(JsonReader in) throws IOException { public MyClass read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
in.nextNull(); in.nextNull();
return null; 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. 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.
## <a id="serialize-nulls"></a> Properties missing in JSON ## <a id="serialize-nulls"></a> Properties missing in JSON
**Symptom:** Properties are missing in the JSON output **Symptom:** Properties are missing in the JSON output
**Reason:** Gson by default omits JSON null from the output (or: ProGuard / R8 is not configured correctly and removed unused fields) **Reason:** Gson by default omits JSON null from the output (or: ProGuard / R8 is not configured correctly and removed unused fields)
**Solution:** Use [`GsonBuilder.serializeNulls()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#serializeNulls()) **Solution:** Use [`GsonBuilder.serializeNulls()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#serializeNulls())
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). 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).
## <a id="android-internal-fields"></a> 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 **Symptom:** The JSON output differs when running on newer Android versions
**Reason:** You use Gson by accident to access internal fields of Android classes **Reason:** You use Gson by accident to access internal fields of Android classes
**Solution:** Write custom Gson [`TypeAdapter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) implementations for the affected classes or change the type of your data **Solution:** Write custom Gson [`TypeAdapter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) implementations for the affected classes or change the type of your data
**Explanation:** **Explanation:**
When no built-in adapter for a type exists and no custom adapter has been registered, Gson falls back to using reflection to access the fields of a class (including `private` ones). Most likely you are experiencing this issue because you (by accident) rely on the reflection-based adapter for Android classes. That should be avoided because you make yourself dependent on the implementation details of these classes which could change at any point. When no built-in adapter for a type exists and no custom adapter has been registered, Gson falls back to using reflection to access the fields of a class (including `private` ones). Most likely you are experiencing this issue because you (by accident) rely on the reflection-based adapter for Android classes. That should be avoided because you make yourself dependent on the implementation details of these classes which could change at any point.
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`. 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`.
## <a id="json-static-fields"></a> 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 **Symptom:** The JSON output contains values of `static` fields
**Reason:** You used `GsonBuilder.excludeFieldsWithModifiers` to overwrite the default excluded modifiers **Reason:** You used `GsonBuilder.excludeFieldsWithModifiers` to overwrite the default excluded modifiers
**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. **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.
## <a id="no-such-method-error"></a> `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 **Symptom:** A `java.lang.NoSuchMethodError` is thrown when trying to call certain Gson methods
**Reason:** **Reason:**
- You have multiple versions of Gson on your classpath - You have multiple versions of Gson on your classpath
- Or, the Gson version you compiled against is different from the one on your classpath - Or, the Gson version you compiled against is different from the one on your classpath
- Or, you are using a code shrinking tool such as ProGuard or R8 which removed methods from Gson - Or, you are using a code shrinking tool such as ProGuard or R8 which removed methods from Gson
**Solution:** First disable any code shrinking tools such as ProGuard or R8 and check if the issue persists. If not, you have to tweak the configuration of that tool to not modify Gson classes. Otherwise verify that the Gson JAR on your classpath is the same you are compiling against, and that there is only one Gson JAR on your classpath. See [this Stack Overflow question](https://stackoverflow.com/q/227486) to find out where a class is loaded from. For example, for debugging you could include the following code: **Solution:** First disable any code shrinking tools such as ProGuard or R8 and check if the issue persists. If not, you have to tweak the configuration of that tool to not modify Gson classes. Otherwise verify that the Gson JAR on your classpath is the same you are compiling against, and that there is only one Gson JAR on your classpath. See [this Stack Overflow question](https://stackoverflow.com/q/227486) to find out where a class is loaded from. For example, for debugging you could include the following code:
```java ```java
System.out.println(Gson.class.getProtectionDomain().getCodeSource().getLocation()); 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. 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 '...'' ## <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 **Symptom:** An exception with the message 'Class ... declares multiple JSON fields named '...'' is thrown
**Reason:** **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 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 - 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 - 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. 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. **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` ## <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` **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. **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: **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 ```java
Class.forName(jsonString, false, getClass().getClassLoader()).asSubclass(MyBaseClass.class) 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`. 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`.
## <a id="type-token-raw"></a> `IllegalStateException`: 'TypeToken must be created with a type argument' <br> `RuntimeException`: 'Missing type parameter' ## <a id="type-token-raw"></a> `IllegalStateException`: 'TypeToken must be created with a type argument' <br> `RuntimeException`: 'Missing type parameter'
**Symptom:** An `IllegalStateException` with the message 'TypeToken must be created with a type argument' is thrown. **Symptom:** An `IllegalStateException` with the message 'TypeToken must be created with a type argument' is thrown.
For older Gson versions a `RuntimeException` with message 'Missing type parameter' is thrown. For older Gson versions a `RuntimeException` with message 'Missing type parameter' is thrown.
**Reason:** **Reason:**
- You created a `TypeToken` without type argument, for example `new TypeToken() {}` (note the missing `<...>`). You always have to provide the type argument, for example like this: `new TypeToken<List<String>>() {}`. Normally the compiler will also emit a 'raw types' warning when you forget the `<...>`. - You created a `TypeToken` without type argument, for example `new TypeToken() {}` (note the missing `<...>`). You always have to provide the type argument, for example like this: `new TypeToken<List<String>>() {}`. Normally the compiler will also emit a 'raw types' warning when you forget the `<...>`.
- You are using a code shrinking tool such as ProGuard or R8 (Android app builds normally have this enabled by default) but have not configured it correctly for usage with Gson. - You are using a code shrinking tool such as ProGuard or R8 (Android app builds normally have this enabled by default) but have not configured it correctly for usage with Gson.
**Solution:** When you are using a code shrinking tool such as ProGuard or R8 you have to adjust your configuration to include the following rules: **Solution:** When you are using a code shrinking tool such as ProGuard or R8 you have to adjust your configuration to include the following rules:
``` ```
# Keep generic signatures; needed for correct type resolution # Keep generic signatures; needed for correct type resolution
-keepattributes Signature -keepattributes Signature
# Keep class TypeToken (respectively its generic signature) # Keep class TypeToken (respectively its generic signature)
-keep class com.google.gson.reflect.TypeToken { *; } -keep class com.google.gson.reflect.TypeToken { *; }
# Keep any (anonymous) classes extending TypeToken # Keep any (anonymous) classes extending TypeToken
-keep class * extends com.google.gson.reflect.TypeToken -keep class * extends com.google.gson.reflect.TypeToken
``` ```
See also the [Android example](examples/android-proguard-example/README.md) for more information. See also the [Android example](examples/android-proguard-example/README.md) for more information.
Note: For newer Gson versions these rules might be applied automatically; make sure you are using the latest Gson version and the latest version of the code shrinking tool. Note: For newer Gson versions these rules might be applied automatically; make sure you are using the latest Gson version and the latest version of the code shrinking tool.
## <a id="r8-abstract-class"></a> `JsonIOException`: 'Abstract classes can't be instantiated!' (R8) ## <a id="r8-abstract-class"></a> `JsonIOException`: 'Abstract classes can't be instantiated!' (R8)
**Symptom:** A `JsonIOException` with the message 'Abstract classes can't be instantiated!' is thrown; the class mentioned in the exception message is not actually `abstract` in your source code, and you are using the code shrinking tool R8 (Android app builds normally have this configured by default). **Symptom:** A `JsonIOException` with the message 'Abstract classes can't be instantiated!' is thrown; the class mentioned in the exception message is not actually `abstract` in your source code, and you are using the code shrinking tool R8 (Android app builds normally have this configured by default).
Note: If the class which you are trying to deserialize is actually abstract, then this exception is probably unrelated to R8 and you will have to implement a custom [`InstanceCreator`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/InstanceCreator.html) or [`TypeAdapter`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) which creates an instance of a non-abstract subclass of the class. Note: If the class which you are trying to deserialize is actually abstract, then this exception is probably unrelated to R8 and you will have to implement a custom [`InstanceCreator`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/InstanceCreator.html) or [`TypeAdapter`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) which creates an instance of a non-abstract subclass of the class.
**Reason:** The code shrinking tool R8 performs optimizations where it removes the no-args constructor from a class and makes the class `abstract`. Due to this Gson cannot create an instance of the class. **Reason:** The code shrinking tool R8 performs optimizations where it removes the no-args constructor from a class and makes the class `abstract`. Due to this Gson cannot create an instance of the class.
**Solution:** Make sure the class has a no-args constructor, then adjust your R8 configuration file to keep the constructor of the class. For example: **Solution:** Make sure the class has a no-args constructor, then adjust your R8 configuration file to keep the constructor of the class. For example:
``` ```
# Keep the no-args constructor of the deserialized class # Keep the no-args constructor of the deserialized class
-keepclassmembers class com.example.MyClass { -keepclassmembers class com.example.MyClass {
<init>(); <init>();
} }
``` ```
You can also use `<init>(...);` to keep all constructors of that class, but then you might actually rely on `sun.misc.Unsafe` on both JDK and Android to create classes without no-args constructor, see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()) for more information. You can also use `<init>(...);` to keep all constructors of that class, but then you might actually rely on `sun.misc.Unsafe` on both JDK and Android to create classes without no-args constructor, see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()) for more information.
For Android you can add this rule to the `proguard-rules.pro` file, see also the [Android documentation](https://developer.android.com/build/shrink-code#keep-code). In case the class name in the exception message is obfuscated, see the Android documentation about [retracing](https://developer.android.com/build/shrink-code#retracing). For Android you can add this rule to the `proguard-rules.pro` file, see also the [Android documentation](https://developer.android.com/build/shrink-code#keep-code). In case the class name in the exception message is obfuscated, see the Android documentation about [retracing](https://developer.android.com/build/shrink-code#retracing).
For Android you can alternatively use the [`@Keep` annotation](https://developer.android.com/studio/write/annotations#keep) on the class or constructor you want to keep. That might be easier than having to maintain a custom R8 configuration. For Android you can alternatively use the [`@Keep` annotation](https://developer.android.com/studio/write/annotations#keep) on the class or constructor you want to keep. That might be easier than having to maintain a custom R8 configuration.
Note that the latest Gson versions (> 2.10.1) specify a default R8 configuration. If your class is a top-level class or is `static`, has a no-args constructor and its fields are annotated with Gson's [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html), you might not have to perform any additional R8 configuration. Note that the latest Gson versions (> 2.10.1) specify a default R8 configuration. If your class is a top-level class or is `static`, has a no-args constructor and its fields are annotated with Gson's [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html), you might not have to perform any additional R8 configuration.
## <a id="typetoken-type-variable"></a> `IllegalArgumentException`: 'TypeToken type argument must not contain a type variable' ## <a id="typetoken-type-variable"></a> `IllegalArgumentException`: 'TypeToken type argument must not contain a type variable'
**Symptom:** An exception with the message 'TypeToken type argument must not contain a type variable' is thrown **Symptom:** An exception with the message 'TypeToken type argument must not contain a type variable' is thrown
**Reason:** This exception is thrown when you create an anonymous `TypeToken` subclass which captures a type variable, for example `new TypeToken<List<T>>() {}` (where `T` is a type variable). At compile time such code looks safe and you can use the type `List<T>` without any warnings. However, this code is not actually type-safe because at runtime due to [type erasure](https://dev.java/learn/generics/type-erasure/) only the upper bound of the type variable is available. For the previous example that would be `List<Object>`. When using such a `TypeToken` with any Gson methods performing deserialization this would lead to confusing and difficult to debug `ClassCastException`s. For serialization it can in some cases also lead to undesired results. **Reason:** This exception is thrown when you create an anonymous `TypeToken` subclass which captures a type variable, for example `new TypeToken<List<T>>() {}` (where `T` is a type variable). At compile time such code looks safe and you can use the type `List<T>` without any warnings. However, this code is not actually type-safe because at runtime due to [type erasure](https://dev.java/learn/generics/type-erasure/) only the upper bound of the type variable is available. For the previous example that would be `List<Object>`. When using such a `TypeToken` with any Gson methods performing deserialization this would lead to confusing and difficult to debug `ClassCastException`s. For serialization it can in some cases also lead to undesired results.
Note: Earlier version of Gson unfortunately did not prevent capturing type variables, which caused many users to unwittingly write type-unsafe code. Note: Earlier version of Gson unfortunately did not prevent capturing type variables, which caused many users to unwittingly write type-unsafe code.
**Solution:** **Solution:**
- 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, elementType)` where `elementType` is a type you have to provide separately. - 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, elementType)` where `elementType` is a type you have to provide separately.
- For Kotlin users: Use [`reified` type parameters](https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters), that means change `<T>` to `<reified T>`, if possible. If you have a chain of functions with type parameters you will probably have to make all of them `reified`. - For Kotlin users: Use [`reified` type parameters](https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters), that means change `<T>` to `<reified T>`, if possible. If you have a chain of functions with type parameters you will probably have to make all of them `reified`.
- If you don't actually use Gson's `TypeToken` for any Gson method, use a general purpose 'type token' implementation provided by a different library instead, for example Guava's [`com.google.common.reflect.TypeToken`](https://javadoc.io/doc/com.google.guava/guava/latest/com/google/common/reflect/TypeToken.html). - If you don't actually use Gson's `TypeToken` for any Gson method, use a general purpose 'type token' implementation provided by a different library instead, for example Guava's [`com.google.common.reflect.TypeToken`](https://javadoc.io/doc/com.google.guava/guava/latest/com/google/common/reflect/TypeToken.html).
For backward compatibility it is possible to restore Gson's old behavior of allowing `TypeToken` to capture type variables by setting the [system property](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setProperty(java.lang.String,java.lang.String)) `gson.allowCapturingTypeVariables` to `"true"`, **however**: For backward compatibility it is possible to restore Gson's old behavior of allowing `TypeToken` to capture type variables by setting the [system property](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setProperty(java.lang.String,java.lang.String)) `gson.allowCapturingTypeVariables` to `"true"`, **however**:
- This does not solve any of the type-safety problems mentioned above; in the long term you should prefer one of the other solutions listed above. This system property might be removed in future Gson versions. - This does not solve any of the type-safety problems mentioned above; in the long term you should prefer one of the other solutions listed above. This system property might be removed in future Gson versions.
- You should only ever set the property to `"true"`, but never to any other value or manually clear it. Otherwise this might counteract any libraries you are using which might have deliberately set the system property because they rely on its behavior. - You should only ever set the property to `"true"`, but never to any other value or manually clear it. Otherwise this might counteract any libraries you are using which might have deliberately set the system property because they rely on its behavior.

View File

@ -1,65 +1,65 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson; package com.google.gson;
/** /**
* A class representing a JSON {@code null} value. * A class representing a JSON {@code null} value.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
* @since 1.2 * @since 1.2
*/ */
public final class JsonNull extends JsonElement { public final class JsonNull extends JsonElement {
/** /**
* Singleton for {@code JsonNull}. * Singleton for {@code JsonNull}.
* *
* @since 1.8 * @since 1.8
*/ */
public static final JsonNull INSTANCE = new JsonNull(); public static final JsonNull INSTANCE = new JsonNull();
/** /**
* Creates a new {@code JsonNull} object. * Creates a new {@code JsonNull} object.
* *
* @deprecated Deprecated since Gson version 1.8, use {@link #INSTANCE} instead. * @deprecated Deprecated since Gson version 1.8, use {@link #INSTANCE} instead.
*/ */
@Deprecated @Deprecated
public JsonNull() { public JsonNull() {
// Do nothing // Do nothing
} }
/** /**
* Returns the same instance since it is an immutable value. * Returns the same instance since it is an immutable value.
* *
* @since 2.8.2 * @since 2.8.2
*/ */
@Override @Override
public JsonNull deepCopy() { public JsonNull deepCopy() {
return INSTANCE; return INSTANCE;
} }
/** All instances of {@code JsonNull} have the same hash code since they are indistinguishable. */ /** All instances of {@code JsonNull} have the same hash code since they are indistinguishable. */
@Override @Override
public int hashCode() { public int hashCode() {
return JsonNull.class.hashCode(); return JsonNull.class.hashCode();
} }
/** All instances of {@code JsonNull} are considered equal. */ /** All instances of {@code JsonNull} are considered equal. */
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return other instanceof JsonNull; return other instanceof JsonNull;
} }
} }

View File

@ -1,139 +1,139 @@
/* /*
* Copyright (C) 2009 Google Inc. * Copyright (C) 2009 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson; package com.google.gson;
import com.google.errorprone.annotations.InlineMe; import com.google.errorprone.annotations.InlineMe;
import com.google.gson.internal.Streams; import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonToken;
import com.google.gson.stream.MalformedJsonException; import com.google.gson.stream.MalformedJsonException;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
/** /**
* A parser to parse JSON into a parse tree of {@link JsonElement}s. * A parser to parse JSON into a parse tree of {@link JsonElement}s.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
* @since 1.3 * @since 1.3
*/ */
public final class JsonParser { public final class JsonParser {
/** /**
* @deprecated No need to instantiate this class, use the static methods instead. * @deprecated No need to instantiate this class, use the static methods instead.
*/ */
@Deprecated @Deprecated
public JsonParser() {} public JsonParser() {}
/** /**
* Parses the specified JSON string into a parse tree. An exception is thrown if the JSON string * Parses the specified JSON string into a parse tree. An exception is thrown if the JSON string
* has multiple top-level JSON elements, or if there is trailing data. * has multiple top-level JSON elements, or if there is trailing data.
* *
* <p>The JSON string is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * <p>The JSON string is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}.
* *
* @param json JSON text * @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON * @throws JsonParseException if the specified text is not valid JSON
* @since 2.8.6 * @since 2.8.6
*/ */
public static JsonElement parseString(String json) throws JsonSyntaxException { public static JsonElement parseString(String json) throws JsonSyntaxException {
return parseReader(new StringReader(json)); return parseReader(new StringReader(json));
} }
/** /**
* Parses the complete JSON string provided by the reader into a parse tree. An exception is * Parses the complete JSON string provided by the reader into a parse tree. An exception is
* thrown if the JSON string has multiple top-level JSON elements, or if there is trailing data. * thrown if the JSON string has multiple top-level JSON elements, or if there is trailing data.
* *
* <p>The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * <p>The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}.
* *
* @param reader JSON text * @param reader JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if there is an IOException or if the specified text is not valid * @throws JsonParseException if there is an IOException or if the specified text is not valid
* JSON * JSON
* @since 2.8.6 * @since 2.8.6
*/ */
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException { public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
try { try {
JsonReader jsonReader = new JsonReader(reader); JsonReader jsonReader = new JsonReader(reader);
JsonElement element = parseReader(jsonReader); JsonElement element = parseReader(jsonReader);
if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) { if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonSyntaxException("Did not consume the entire document."); throw new JsonSyntaxException("Did not consume the entire document.");
} }
return element; return element;
} catch (MalformedJsonException e) { } catch (MalformedJsonException e) {
throw new JsonSyntaxException(e); throw new JsonSyntaxException(e);
} catch (IOException e) { } catch (IOException e) {
throw new JsonIOException(e); throw new JsonIOException(e);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new JsonSyntaxException(e); throw new JsonSyntaxException(e);
} }
} }
/** /**
* Returns the next value from the JSON stream as a parse tree. Unlike the other {@code parse} * Returns the next value from the JSON stream as a parse tree. Unlike the other {@code parse}
* methods, no exception is thrown if the JSON data has multiple top-level JSON elements, or if * methods, no exception is thrown if the JSON data has multiple top-level JSON elements, or if
* there is trailing data. * there is trailing data.
* *
* <p>The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}, * <p>The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode},
* regardless of the strictness setting of the provided reader. The strictness setting of the * regardless of the strictness setting of the provided reader. The strictness setting of the
* reader is restored once this method returns. * reader is restored once this method returns.
* *
* @throws JsonParseException if there is an IOException or if the specified text is not valid * @throws JsonParseException if there is an IOException or if the specified text is not valid
* JSON * JSON
* @since 2.8.6 * @since 2.8.6
*/ */
public static JsonElement parseReader(JsonReader reader) public static JsonElement parseReader(JsonReader reader)
throws JsonIOException, JsonSyntaxException { throws JsonIOException, JsonSyntaxException {
Strictness strictness = reader.getStrictness(); Strictness strictness = reader.getStrictness();
reader.setStrictness(Strictness.LENIENT); reader.setStrictness(Strictness.LENIENT);
try { try {
return Streams.parse(reader); return Streams.parse(reader);
} catch (StackOverflowError e) { } catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e); throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e); throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
} finally { } finally {
reader.setStrictness(strictness); reader.setStrictness(strictness);
} }
} }
/** /**
* @deprecated Use {@link JsonParser#parseString} * @deprecated Use {@link JsonParser#parseString}
*/ */
@Deprecated @Deprecated
@InlineMe(replacement = "JsonParser.parseString(json)", imports = "com.google.gson.JsonParser") @InlineMe(replacement = "JsonParser.parseString(json)", imports = "com.google.gson.JsonParser")
public JsonElement parse(String json) throws JsonSyntaxException { public JsonElement parse(String json) throws JsonSyntaxException {
return parseString(json); return parseString(json);
} }
/** /**
* @deprecated Use {@link JsonParser#parseReader(Reader)} * @deprecated Use {@link JsonParser#parseReader(Reader)}
*/ */
@Deprecated @Deprecated
@InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser") @InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser")
public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException { public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
return parseReader(json); return parseReader(json);
} }
/** /**
* @deprecated Use {@link JsonParser#parseReader(JsonReader)} * @deprecated Use {@link JsonParser#parseReader(JsonReader)}
*/ */
@Deprecated @Deprecated
@InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser") @InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser")
public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException { public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
return parseReader(json); return parseReader(json);
} }
} }

View File

@ -1,57 +1,57 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.internal; package com.google.gson.internal;
import java.util.Objects; import java.util.Objects;
/** /**
* A simple utility class used to check method Preconditions. * A simple utility class used to check method Preconditions.
* *
* <pre> * <pre>
* public long divideBy(long value) { * public long divideBy(long value) {
* Preconditions.checkArgument(value != 0); * Preconditions.checkArgument(value != 0);
* return this.value / value; * return this.value / value;
* } * }
* </pre> * </pre>
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
public final class $Gson$Preconditions { public final class $Gson$Preconditions {
private $Gson$Preconditions() { private $Gson$Preconditions() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
/** /**
* @deprecated This is an internal Gson method. Use {@link Objects#requireNonNull(Object)} * @deprecated This is an internal Gson method. Use {@link Objects#requireNonNull(Object)}
* instead. * instead.
*/ */
// Only deprecated for now because external projects might be using this by accident // Only deprecated for now because external projects might be using this by accident
@Deprecated @Deprecated
public static <T> T checkNotNull(T obj) { public static <T> T checkNotNull(T obj) {
if (obj == null) { if (obj == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
return obj; return obj;
} }
public static void checkArgument(boolean condition) { public static void checkArgument(boolean condition) {
if (!condition) { if (!condition) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
} }
} }

View File

@ -1,103 +1,103 @@
/* /*
* Copyright (C) 2011 Google Inc. * Copyright (C) 2011 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.internal.bind; package com.google.gson.internal.bind;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable; import java.lang.reflect.TypeVariable;
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> { final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final Gson context; private final Gson context;
private final TypeAdapter<T> delegate; private final TypeAdapter<T> delegate;
private final Type type; private final Type type;
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) { TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
this.context = context; this.context = context;
this.delegate = delegate; this.delegate = delegate;
this.type = type; this.type = type;
} }
@Override @Override
public T read(JsonReader in) throws IOException { public T read(JsonReader in) throws IOException {
return delegate.read(in); return delegate.read(in);
} }
@Override @Override
public void write(JsonWriter out, T value) throws IOException { public void write(JsonWriter out, T value) throws IOException {
// Order of preference for choosing type adapters // Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type // First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type // Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a subclass of the // Third preference: reflective type adapter for the runtime type (if it is a subclass of the
// declared type) // declared type)
// Fourth preference: reflective type adapter for the declared type // Fourth preference: reflective type adapter for the declared type
TypeAdapter<T> chosen = delegate; TypeAdapter<T> chosen = delegate;
Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value); Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) { if (runtimeType != type) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
TypeAdapter<T> runtimeTypeAdapter = TypeAdapter<T> runtimeTypeAdapter =
(TypeAdapter<T>) context.getAdapter(TypeToken.get(runtimeType)); (TypeAdapter<T>) context.getAdapter(TypeToken.get(runtimeType));
// For backward compatibility only check ReflectiveTypeAdapterFactory.Adapter here but not any // For backward compatibility only check ReflectiveTypeAdapterFactory.Adapter here but not any
// other wrapping adapters, see // other wrapping adapters, see
// https://github.com/google/gson/pull/1787#issuecomment-1222175189 // https://github.com/google/gson/pull/1787#issuecomment-1222175189
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) { if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that // The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter; chosen = runtimeTypeAdapter;
} else if (!isReflective(delegate)) { } else if (!isReflective(delegate)) {
// The user registered a type adapter for Base class, so we prefer it over the // The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type // reflective type adapter for the runtime type
chosen = delegate; chosen = delegate;
} else { } else {
// Use the type adapter for runtime type // Use the type adapter for runtime type
chosen = runtimeTypeAdapter; chosen = runtimeTypeAdapter;
} }
} }
chosen.write(out, value); chosen.write(out, value);
} }
/** /**
* Returns whether the type adapter uses reflection. * Returns whether the type adapter uses reflection.
* *
* @param typeAdapter the type adapter to check. * @param typeAdapter the type adapter to check.
*/ */
private static boolean isReflective(TypeAdapter<?> typeAdapter) { private static boolean isReflective(TypeAdapter<?> typeAdapter) {
// Run this in loop in case multiple delegating adapters are nested // Run this in loop in case multiple delegating adapters are nested
while (typeAdapter instanceof SerializationDelegatingTypeAdapter) { while (typeAdapter instanceof SerializationDelegatingTypeAdapter) {
TypeAdapter<?> delegate = TypeAdapter<?> delegate =
((SerializationDelegatingTypeAdapter<?>) typeAdapter).getSerializationDelegate(); ((SerializationDelegatingTypeAdapter<?>) typeAdapter).getSerializationDelegate();
// Break if adapter does not delegate serialization // Break if adapter does not delegate serialization
if (delegate == typeAdapter) { if (delegate == typeAdapter) {
break; break;
} }
typeAdapter = delegate; typeAdapter = delegate;
} }
return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter; return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter;
} }
/** Finds a compatible runtime type if it is more specific */ /** Finds a compatible runtime type if it is more specific */
private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
if (value != null && (type instanceof Class<?> || type instanceof TypeVariable<?>)) { if (value != null && (type instanceof Class<?> || type instanceof TypeVariable<?>)) {
type = value.getClass(); type = value.getClass();
} }
return type; return type;
} }
} }

View File

@ -1,308 +1,308 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson; package com.google.gson;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import org.junit.Test; import org.junit.Test;
/** /**
* Unit tests for {@link GsonBuilder}. * Unit tests for {@link GsonBuilder}.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
*/ */
public class GsonBuilderTest { public class GsonBuilderTest {
private static final TypeAdapter<Object> NULL_TYPE_ADAPTER = private static final TypeAdapter<Object> NULL_TYPE_ADAPTER =
new TypeAdapter<Object>() { new TypeAdapter<Object>() {
@Override @Override
public void write(JsonWriter out, Object value) { public void write(JsonWriter out, Object value) {
throw new AssertionError(); throw new AssertionError();
} }
@Override @Override
public Object read(JsonReader in) { public Object read(JsonReader in) {
throw new AssertionError(); throw new AssertionError();
} }
}; };
@Test @Test
public void testCreatingMoreThanOnce() { public void testCreatingMoreThanOnce() {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create(); Gson gson = builder.create();
assertThat(gson).isNotNull(); assertThat(gson).isNotNull();
assertThat(builder.create()).isNotNull(); assertThat(builder.create()).isNotNull();
builder.setFieldNamingStrategy( builder.setFieldNamingStrategy(
new FieldNamingStrategy() { new FieldNamingStrategy() {
@Override @Override
public String translateName(Field f) { public String translateName(Field f) {
return "test"; return "test";
} }
}); });
Gson otherGson = builder.create(); Gson otherGson = builder.create();
assertThat(otherGson).isNotNull(); assertThat(otherGson).isNotNull();
// Should be different instances because builder has been modified in the meantime // Should be different instances because builder has been modified in the meantime
assertThat(gson).isNotSameInstanceAs(otherGson); assertThat(gson).isNotSameInstanceAs(otherGson);
} }
/** /**
* Gson instances should not be affected by subsequent modification of GsonBuilder which created * Gson instances should not be affected by subsequent modification of GsonBuilder which created
* them. * them.
*/ */
@Test @Test
public void testModificationAfterCreate() { public void testModificationAfterCreate() {
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
// Modifications of `gsonBuilder` should not affect `gson` object // Modifications of `gsonBuilder` should not affect `gson` object
gsonBuilder.registerTypeAdapter( gsonBuilder.registerTypeAdapter(
CustomClass1.class, CustomClass1.class,
new TypeAdapter<CustomClass1>() { new TypeAdapter<CustomClass1>() {
@Override @Override
public CustomClass1 read(JsonReader in) { public CustomClass1 read(JsonReader in) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void write(JsonWriter out, CustomClass1 value) throws IOException { public void write(JsonWriter out, CustomClass1 value) throws IOException {
out.value("custom-adapter"); out.value("custom-adapter");
} }
}); });
gsonBuilder.registerTypeHierarchyAdapter( gsonBuilder.registerTypeHierarchyAdapter(
CustomClass2.class, CustomClass2.class,
new JsonSerializer<CustomClass2>() { new JsonSerializer<CustomClass2>() {
@Override @Override
public JsonElement serialize( public JsonElement serialize(
CustomClass2 src, Type typeOfSrc, JsonSerializationContext context) { CustomClass2 src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive("custom-hierarchy-adapter"); return new JsonPrimitive("custom-hierarchy-adapter");
} }
}); });
gsonBuilder.registerTypeAdapter( gsonBuilder.registerTypeAdapter(
CustomClass3.class, CustomClass3.class,
new InstanceCreator<CustomClass3>() { new InstanceCreator<CustomClass3>() {
@Override @Override
public CustomClass3 createInstance(Type type) { public CustomClass3 createInstance(Type type) {
return new CustomClass3("custom-instance"); return new CustomClass3("custom-instance");
} }
}); });
assertDefaultGson(gson); assertDefaultGson(gson);
// New GsonBuilder created from `gson` should not have been affected by changes // New GsonBuilder created from `gson` should not have been affected by changes
// to `gsonBuilder` either // to `gsonBuilder` either
assertDefaultGson(gson.newBuilder().create()); assertDefaultGson(gson.newBuilder().create());
// New Gson instance from modified GsonBuilder should be affected by changes // New Gson instance from modified GsonBuilder should be affected by changes
assertCustomGson(gsonBuilder.create()); assertCustomGson(gsonBuilder.create());
} }
private static void assertDefaultGson(Gson gson) { private static void assertDefaultGson(Gson gson) {
// Should use default reflective adapter // Should use default reflective adapter
String json1 = gson.toJson(new CustomClass1()); String json1 = gson.toJson(new CustomClass1());
assertThat(json1).isEqualTo("{}"); assertThat(json1).isEqualTo("{}");
// Should use default reflective adapter // Should use default reflective adapter
String json2 = gson.toJson(new CustomClass2()); String json2 = gson.toJson(new CustomClass2());
assertThat(json2).isEqualTo("{}"); assertThat(json2).isEqualTo("{}");
// Should use default instance creator // Should use default instance creator
CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class);
assertThat(customClass3.s).isEqualTo(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE); assertThat(customClass3.s).isEqualTo(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE);
} }
private static void assertCustomGson(Gson gson) { private static void assertCustomGson(Gson gson) {
String json1 = gson.toJson(new CustomClass1()); String json1 = gson.toJson(new CustomClass1());
assertThat(json1).isEqualTo("\"custom-adapter\""); assertThat(json1).isEqualTo("\"custom-adapter\"");
String json2 = gson.toJson(new CustomClass2()); String json2 = gson.toJson(new CustomClass2());
assertThat(json2).isEqualTo("\"custom-hierarchy-adapter\""); assertThat(json2).isEqualTo("\"custom-hierarchy-adapter\"");
CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class);
assertThat(customClass3.s).isEqualTo("custom-instance"); assertThat(customClass3.s).isEqualTo("custom-instance");
} }
static class CustomClass1 {} static class CustomClass1 {}
static class CustomClass2 {} static class CustomClass2 {}
static class CustomClass3 { static class CustomClass3 {
static final String NO_ARG_CONSTRUCTOR_VALUE = "default instance"; static final String NO_ARG_CONSTRUCTOR_VALUE = "default instance";
final String s; final String s;
public CustomClass3(String s) { public CustomClass3(String s) {
this.s = s; this.s = s;
} }
public CustomClass3() { public CustomClass3() {
this(NO_ARG_CONSTRUCTOR_VALUE); this(NO_ARG_CONSTRUCTOR_VALUE);
} }
} }
@Test @Test
public void testExcludeFieldsWithModifiers() { public void testExcludeFieldsWithModifiers() {
Gson gson = Gson gson =
new GsonBuilder().excludeFieldsWithModifiers(Modifier.VOLATILE, Modifier.PRIVATE).create(); new GsonBuilder().excludeFieldsWithModifiers(Modifier.VOLATILE, Modifier.PRIVATE).create();
assertThat(gson.toJson(new HasModifiers())).isEqualTo("{\"d\":\"d\"}"); assertThat(gson.toJson(new HasModifiers())).isEqualTo("{\"d\":\"d\"}");
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
static class HasModifiers { static class HasModifiers {
private String a = "a"; private String a = "a";
volatile String b = "b"; volatile String b = "b";
private volatile String c = "c"; private volatile String c = "c";
String d = "d"; String d = "d";
} }
@Test @Test
public void testTransientFieldExclusion() { public void testTransientFieldExclusion() {
Gson gson = new GsonBuilder().excludeFieldsWithModifiers().create(); Gson gson = new GsonBuilder().excludeFieldsWithModifiers().create();
assertThat(gson.toJson(new HasTransients())).isEqualTo("{\"a\":\"a\"}"); assertThat(gson.toJson(new HasTransients())).isEqualTo("{\"a\":\"a\"}");
} }
static class HasTransients { static class HasTransients {
transient String a = "a"; transient String a = "a";
} }
@Test @Test
public void testRegisterTypeAdapterForCoreType() { public void testRegisterTypeAdapterForCoreType() {
Type[] types = { Type[] types = {
byte.class, int.class, double.class, Short.class, Long.class, String.class, byte.class, int.class, double.class, Short.class, Long.class, String.class,
}; };
for (Type type : types) { for (Type type : types) {
new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER); new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
} }
} }
@Test @Test
public void testDisableJdkUnsafe() { public void testDisableJdkUnsafe() {
Gson gson = new GsonBuilder().disableJdkUnsafe().create(); Gson gson = new GsonBuilder().disableJdkUnsafe().create();
try { try {
gson.fromJson("{}", ClassWithoutNoArgsConstructor.class); gson.fromJson("{}", ClassWithoutNoArgsConstructor.class);
fail("Expected exception"); fail("Expected exception");
} catch (JsonIOException expected) { } catch (JsonIOException expected) {
assertThat(expected) assertThat(expected)
.hasMessageThat() .hasMessageThat()
.isEqualTo( .isEqualTo(
"Unable to create instance of class" "Unable to create instance of class"
+ " com.google.gson.GsonBuilderTest$ClassWithoutNoArgsConstructor; usage of JDK" + " com.google.gson.GsonBuilderTest$ClassWithoutNoArgsConstructor; usage of JDK"
+ " Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter for this" + " Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter for this"
+ " type, adding a no-args constructor, or enabling usage of JDK Unsafe may fix" + " type, adding a no-args constructor, or enabling usage of JDK Unsafe may fix"
+ " this problem."); + " this problem.");
} }
} }
private static class ClassWithoutNoArgsConstructor { private static class ClassWithoutNoArgsConstructor {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public ClassWithoutNoArgsConstructor(String s) {} public ClassWithoutNoArgsConstructor(String s) {}
} }
@Test @Test
public void testSetVersionInvalid() { public void testSetVersionInvalid() {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
try { try {
builder.setVersion(Double.NaN); builder.setVersion(Double.NaN);
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("Invalid version: NaN"); assertThat(e).hasMessageThat().isEqualTo("Invalid version: NaN");
} }
try { try {
builder.setVersion(-0.1); builder.setVersion(-0.1);
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("Invalid version: -0.1"); assertThat(e).hasMessageThat().isEqualTo("Invalid version: -0.1");
} }
} }
@Test @Test
public void testDefaultStrictness() throws IOException { public void testDefaultStrictness() throws IOException {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create(); Gson gson = builder.create();
assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness()) assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness())
.isEqualTo(Strictness.LEGACY_STRICT); .isEqualTo(Strictness.LEGACY_STRICT);
assertThat(gson.newJsonWriter(new StringWriter()).getStrictness()) assertThat(gson.newJsonWriter(new StringWriter()).getStrictness())
.isEqualTo(Strictness.LEGACY_STRICT); .isEqualTo(Strictness.LEGACY_STRICT);
} }
@SuppressWarnings({"deprecation", "InlineMeInliner"}) // for GsonBuilder.setLenient @SuppressWarnings({"deprecation", "InlineMeInliner"}) // for GsonBuilder.setLenient
@Test @Test
public void testSetLenient() throws IOException { public void testSetLenient() throws IOException {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
builder.setLenient(); builder.setLenient();
Gson gson = builder.create(); Gson gson = builder.create();
assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness()) assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness())
.isEqualTo(Strictness.LENIENT); .isEqualTo(Strictness.LENIENT);
assertThat(gson.newJsonWriter(new StringWriter()).getStrictness()) assertThat(gson.newJsonWriter(new StringWriter()).getStrictness())
.isEqualTo(Strictness.LENIENT); .isEqualTo(Strictness.LENIENT);
} }
@Test @Test
public void testSetStrictness() throws IOException { public void testSetStrictness() throws IOException {
final Strictness STRICTNESS = Strictness.STRICT; final Strictness STRICTNESS = Strictness.STRICT;
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
builder.setStrictness(STRICTNESS); builder.setStrictness(STRICTNESS);
Gson gson = builder.create(); Gson gson = builder.create();
assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness()).isEqualTo(STRICTNESS); assertThat(gson.newJsonReader(new StringReader("{}")).getStrictness()).isEqualTo(STRICTNESS);
assertThat(gson.newJsonWriter(new StringWriter()).getStrictness()).isEqualTo(STRICTNESS); assertThat(gson.newJsonWriter(new StringWriter()).getStrictness()).isEqualTo(STRICTNESS);
} }
@Test @Test
public void testRegisterTypeAdapterForObjectAndJsonElements() { public void testRegisterTypeAdapterForObjectAndJsonElements() {
final String ERROR_MESSAGE = "Cannot override built-in adapter for "; final String ERROR_MESSAGE = "Cannot override built-in adapter for ";
Type[] types = { Type[] types = {
Object.class, JsonElement.class, JsonArray.class, Object.class, JsonElement.class, JsonArray.class,
}; };
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
for (Type type : types) { for (Type type : types) {
IllegalArgumentException e = IllegalArgumentException e =
assertThrows( assertThrows(
IllegalArgumentException.class, IllegalArgumentException.class,
() -> gsonBuilder.registerTypeAdapter(type, NULL_TYPE_ADAPTER)); () -> gsonBuilder.registerTypeAdapter(type, NULL_TYPE_ADAPTER));
assertThat(e).hasMessageThat().isEqualTo(ERROR_MESSAGE + type); assertThat(e).hasMessageThat().isEqualTo(ERROR_MESSAGE + type);
} }
} }
@Test @Test
public void testRegisterTypeHierarchyAdapterJsonElements() { public void testRegisterTypeHierarchyAdapterJsonElements() {
final String ERROR_MESSAGE = "Cannot override built-in adapter for "; final String ERROR_MESSAGE = "Cannot override built-in adapter for ";
Class<?>[] types = { Class<?>[] types = {
JsonElement.class, JsonArray.class, JsonElement.class, JsonArray.class,
}; };
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
for (Class<?> type : types) { for (Class<?> type : types) {
IllegalArgumentException e = IllegalArgumentException e =
assertThrows( assertThrows(
IllegalArgumentException.class, IllegalArgumentException.class,
() -> gsonBuilder.registerTypeHierarchyAdapter(type, NULL_TYPE_ADAPTER)); () -> gsonBuilder.registerTypeHierarchyAdapter(type, NULL_TYPE_ADAPTER));
assertThat(e).hasMessageThat().isEqualTo(ERROR_MESSAGE + type); assertThat(e).hasMessageThat().isEqualTo(ERROR_MESSAGE + type);
} }
// But registering type hierarchy adapter for Object should be allowed // But registering type hierarchy adapter for Object should be allowed
gsonBuilder.registerTypeHierarchyAdapter(Object.class, NULL_TYPE_ADAPTER); gsonBuilder.registerTypeHierarchyAdapter(Object.class, NULL_TYPE_ADAPTER);
} }
} }

View File

@ -1,149 +1,149 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.functional; package com.google.gson.functional;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
* Tests for ensuring Gson thread-safety. * Tests for ensuring Gson thread-safety.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
public class ConcurrencyTest { public class ConcurrencyTest {
private Gson gson; private Gson gson;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
gson = new Gson(); gson = new Gson();
} }
/** /**
* Source-code based on * Source-code based on
* http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081 * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
*/ */
@Test @Test
public void testSingleThreadSerialization() { public void testSingleThreadSerialization() {
MyObject myObj = new MyObject(); MyObject myObj = new MyObject();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
String unused = gson.toJson(myObj); String unused = gson.toJson(myObj);
} }
} }
/** /**
* Source-code based on * Source-code based on
* http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081 * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
*/ */
@Test @Test
public void testSingleThreadDeserialization() { public void testSingleThreadDeserialization() {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
MyObject unused = gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class); MyObject unused = gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class);
} }
} }
/** /**
* Source-code based on * Source-code based on
* http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081 * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
*/ */
@Test @Test
public void testMultiThreadSerialization() throws InterruptedException { public void testMultiThreadSerialization() throws InterruptedException {
final CountDownLatch startLatch = new CountDownLatch(1); final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch finishedLatch = new CountDownLatch(10); final CountDownLatch finishedLatch = new CountDownLatch(10);
final AtomicBoolean failed = new AtomicBoolean(false); final AtomicBoolean failed = new AtomicBoolean(false);
ExecutorService executor = Executors.newFixedThreadPool(10); ExecutorService executor = Executors.newFixedThreadPool(10);
for (int taskCount = 0; taskCount < 10; taskCount++) { for (int taskCount = 0; taskCount < 10; taskCount++) {
executor.execute( executor.execute(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
MyObject myObj = new MyObject(); MyObject myObj = new MyObject();
try { try {
startLatch.await(); startLatch.await();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
String unused = gson.toJson(myObj); String unused = gson.toJson(myObj);
} }
} catch (Throwable t) { } catch (Throwable t) {
failed.set(true); failed.set(true);
} finally { } finally {
finishedLatch.countDown(); finishedLatch.countDown();
} }
} }
}); });
} }
startLatch.countDown(); startLatch.countDown();
finishedLatch.await(); finishedLatch.await();
assertThat(failed.get()).isFalse(); assertThat(failed.get()).isFalse();
} }
/** /**
* Source-code based on * Source-code based on
* http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081 * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
*/ */
@Test @Test
public void testMultiThreadDeserialization() throws InterruptedException { public void testMultiThreadDeserialization() throws InterruptedException {
final CountDownLatch startLatch = new CountDownLatch(1); final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch finishedLatch = new CountDownLatch(10); final CountDownLatch finishedLatch = new CountDownLatch(10);
final AtomicBoolean failed = new AtomicBoolean(false); final AtomicBoolean failed = new AtomicBoolean(false);
ExecutorService executor = Executors.newFixedThreadPool(10); ExecutorService executor = Executors.newFixedThreadPool(10);
for (int taskCount = 0; taskCount < 10; taskCount++) { for (int taskCount = 0; taskCount < 10; taskCount++) {
executor.execute( executor.execute(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
startLatch.await(); startLatch.await();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
MyObject unused = MyObject unused =
gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class); gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class);
} }
} catch (Throwable t) { } catch (Throwable t) {
failed.set(true); failed.set(true);
} finally { } finally {
finishedLatch.countDown(); finishedLatch.countDown();
} }
} }
}); });
} }
startLatch.countDown(); startLatch.countDown();
finishedLatch.await(); finishedLatch.await();
assertThat(failed.get()).isFalse(); assertThat(failed.get()).isFalse();
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static class MyObject { private static class MyObject {
String a; String a;
String b; String b;
int i; int i;
MyObject() { MyObject() {
this("hello", "world", 42); this("hello", "world", 42);
} }
public MyObject(String a, String b, int i) { public MyObject(String a, String b, int i) {
this.a = a; this.a = a;
this.b = b; this.b = b;
this.i = i; this.i = i;
} }
} }
} }

View File

@ -1,83 +1,83 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.functional; package com.google.gson.functional;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
* Functional tests for internationalized strings. * Functional tests for internationalized strings.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
*/ */
public class InternationalizationTest { public class InternationalizationTest {
private Gson gson; private Gson gson;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
gson = new Gson(); gson = new Gson();
} }
@Test @Test
public void testStringsWithUnicodeChineseCharactersSerialization() { public void testStringsWithUnicodeChineseCharactersSerialization() {
String target = "\u597d\u597d\u597d"; String target = "\u597d\u597d\u597d";
String json = gson.toJson(target); String json = gson.toJson(target);
String expected = '"' + target + '"'; String expected = '"' + target + '"';
assertThat(json).isEqualTo(expected); assertThat(json).isEqualTo(expected);
} }
@Test @Test
public void testStringsWithUnicodeChineseCharactersDeserialization() { public void testStringsWithUnicodeChineseCharactersDeserialization() {
String expected = "\u597d\u597d\u597d"; String expected = "\u597d\u597d\u597d";
String json = '"' + expected + '"'; String json = '"' + expected + '"';
String actual = gson.fromJson(json, String.class); String actual = gson.fromJson(json, String.class);
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
} }
@Test @Test
public void testStringsWithUnicodeChineseCharactersEscapedDeserialization() { public void testStringsWithUnicodeChineseCharactersEscapedDeserialization() {
String actual = gson.fromJson("'\\u597d\\u597d\\u597d'", String.class); String actual = gson.fromJson("'\\u597d\\u597d\\u597d'", String.class);
assertThat(actual).isEqualTo("\u597d\u597d\u597d"); assertThat(actual).isEqualTo("\u597d\u597d\u597d");
} }
@Test @Test
public void testSupplementaryUnicodeSerialization() { public void testSupplementaryUnicodeSerialization() {
// Supplementary code point U+1F60A // Supplementary code point U+1F60A
String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1); String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1);
String json = gson.toJson(supplementaryCodePoint); String json = gson.toJson(supplementaryCodePoint);
assertThat(json).isEqualTo('"' + supplementaryCodePoint + '"'); assertThat(json).isEqualTo('"' + supplementaryCodePoint + '"');
} }
@Test @Test
public void testSupplementaryUnicodeDeserialization() { public void testSupplementaryUnicodeDeserialization() {
// Supplementary code point U+1F60A // Supplementary code point U+1F60A
String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1); String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1);
String actual = gson.fromJson('"' + supplementaryCodePoint + '"', String.class); String actual = gson.fromJson('"' + supplementaryCodePoint + '"', String.class);
assertThat(actual).isEqualTo(supplementaryCodePoint); assertThat(actual).isEqualTo(supplementaryCodePoint);
} }
@Test @Test
public void testSupplementaryUnicodeEscapedDeserialization() { public void testSupplementaryUnicodeEscapedDeserialization() {
// Supplementary code point U+1F60A // Supplementary code point U+1F60A
String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1); String supplementaryCodePoint = new String(new int[] {0x1F60A}, 0, 1);
String actual = gson.fromJson("\"\\uD83D\\uDE0A\"", String.class); String actual = gson.fromJson("\"\\uD83D\\uDE0A\"", String.class);
assertThat(actual).isEqualTo(supplementaryCodePoint); assertThat(actual).isEqualTo(supplementaryCodePoint);
} }
} }

View File

@ -1,267 +1,267 @@
/* /*
* Copyright (C) 2008 Google Inc. * Copyright (C) 2008 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.functional; package com.google.gson.functional;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer; import com.google.gson.JsonSerializer;
import com.google.gson.common.TestTypes.BagOfPrimitives; import com.google.gson.common.TestTypes.BagOfPrimitives;
import com.google.gson.common.TestTypes.ClassWithObjects; import com.google.gson.common.TestTypes.ClassWithObjects;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection; import java.util.Collection;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
* Functional tests for the different cases for serializing (or ignoring) null fields and object. * Functional tests for the different cases for serializing (or ignoring) null fields and object.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
public class NullObjectAndFieldTest { public class NullObjectAndFieldTest {
private GsonBuilder gsonBuilder; private GsonBuilder gsonBuilder;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
gsonBuilder = new GsonBuilder().serializeNulls(); gsonBuilder = new GsonBuilder().serializeNulls();
} }
@Test @Test
public void testTopLevelNullObjectSerialization() { public void testTopLevelNullObjectSerialization() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
String actual = gson.toJson(null); String actual = gson.toJson(null);
assertThat(actual).isEqualTo("null"); assertThat(actual).isEqualTo("null");
actual = gson.toJson(null, String.class); actual = gson.toJson(null, String.class);
assertThat(actual).isEqualTo("null"); assertThat(actual).isEqualTo("null");
} }
@Test @Test
public void testTopLevelNullObjectDeserialization() { public void testTopLevelNullObjectDeserialization() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
String actual = gson.fromJson("null", String.class); String actual = gson.fromJson("null", String.class);
assertThat(actual).isNull(); assertThat(actual).isNull();
} }
@Test @Test
public void testExplicitSerializationOfNulls() { public void testExplicitSerializationOfNulls() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithObjects target = new ClassWithObjects(null); ClassWithObjects target = new ClassWithObjects(null);
String actual = gson.toJson(target); String actual = gson.toJson(target);
String expected = "{\"bag\":null}"; String expected = "{\"bag\":null}";
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
} }
@Test @Test
public void testExplicitDeserializationOfNulls() { public void testExplicitDeserializationOfNulls() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithObjects target = gson.fromJson("{\"bag\":null}", ClassWithObjects.class); ClassWithObjects target = gson.fromJson("{\"bag\":null}", ClassWithObjects.class);
assertThat(target.bag).isNull(); assertThat(target.bag).isNull();
} }
@Test @Test
public void testExplicitSerializationOfNullArrayMembers() { public void testExplicitSerializationOfNullArrayMembers() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithMembers target = new ClassWithMembers(); ClassWithMembers target = new ClassWithMembers();
String json = gson.toJson(target); String json = gson.toJson(target);
assertThat(json).contains("\"array\":null"); assertThat(json).contains("\"array\":null");
} }
/** Added to verify http://code.google.com/p/google-gson/issues/detail?id=68 */ /** Added to verify http://code.google.com/p/google-gson/issues/detail?id=68 */
@Test @Test
public void testNullWrappedPrimitiveMemberSerialization() { public void testNullWrappedPrimitiveMemberSerialization() {
Gson gson = gsonBuilder.serializeNulls().create(); Gson gson = gsonBuilder.serializeNulls().create();
ClassWithNullWrappedPrimitive target = new ClassWithNullWrappedPrimitive(); ClassWithNullWrappedPrimitive target = new ClassWithNullWrappedPrimitive();
String json = gson.toJson(target); String json = gson.toJson(target);
assertThat(json).contains("\"value\":null"); assertThat(json).contains("\"value\":null");
} }
/** Added to verify http://code.google.com/p/google-gson/issues/detail?id=68 */ /** Added to verify http://code.google.com/p/google-gson/issues/detail?id=68 */
@Test @Test
public void testNullWrappedPrimitiveMemberDeserialization() { public void testNullWrappedPrimitiveMemberDeserialization() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
String json = "{'value':null}"; String json = "{'value':null}";
ClassWithNullWrappedPrimitive target = gson.fromJson(json, ClassWithNullWrappedPrimitive.class); ClassWithNullWrappedPrimitive target = gson.fromJson(json, ClassWithNullWrappedPrimitive.class);
assertThat(target.value).isNull(); assertThat(target.value).isNull();
} }
@Test @Test
public void testExplicitSerializationOfNullCollectionMembers() { public void testExplicitSerializationOfNullCollectionMembers() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithMembers target = new ClassWithMembers(); ClassWithMembers target = new ClassWithMembers();
String json = gson.toJson(target); String json = gson.toJson(target);
assertThat(json).contains("\"col\":null"); assertThat(json).contains("\"col\":null");
} }
@Test @Test
public void testExplicitSerializationOfNullStringMembers() { public void testExplicitSerializationOfNullStringMembers() {
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithMembers target = new ClassWithMembers(); ClassWithMembers target = new ClassWithMembers();
String json = gson.toJson(target); String json = gson.toJson(target);
assertThat(json).contains("\"str\":null"); assertThat(json).contains("\"str\":null");
} }
@Test @Test
public void testCustomSerializationOfNulls() { public void testCustomSerializationOfNulls() {
gsonBuilder.registerTypeAdapter(ClassWithObjects.class, new ClassWithObjectsSerializer()); gsonBuilder.registerTypeAdapter(ClassWithObjects.class, new ClassWithObjectsSerializer());
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
ClassWithObjects target = new ClassWithObjects(new BagOfPrimitives()); ClassWithObjects target = new ClassWithObjects(new BagOfPrimitives());
String actual = gson.toJson(target); String actual = gson.toJson(target);
String expected = "{\"bag\":null}"; String expected = "{\"bag\":null}";
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
} }
@Test @Test
public void testPrintPrintingObjectWithNulls() { public void testPrintPrintingObjectWithNulls() {
gsonBuilder = new GsonBuilder(); gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
String result = gson.toJson(new ClassWithMembers()); String result = gson.toJson(new ClassWithMembers());
assertThat(result).isEqualTo("{}"); assertThat(result).isEqualTo("{}");
gson = gsonBuilder.serializeNulls().create(); gson = gsonBuilder.serializeNulls().create();
result = gson.toJson(new ClassWithMembers()); result = gson.toJson(new ClassWithMembers());
assertThat(result).contains("\"str\":null"); assertThat(result).contains("\"str\":null");
} }
@Test @Test
public void testPrintPrintingArraysWithNulls() { public void testPrintPrintingArraysWithNulls() {
gsonBuilder = new GsonBuilder(); gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create(); Gson gson = gsonBuilder.create();
String result = gson.toJson(new String[] {"1", null, "3"}); String result = gson.toJson(new String[] {"1", null, "3"});
assertThat(result).isEqualTo("[\"1\",null,\"3\"]"); assertThat(result).isEqualTo("[\"1\",null,\"3\"]");
gson = gsonBuilder.serializeNulls().create(); gson = gsonBuilder.serializeNulls().create();
result = gson.toJson(new String[] {"1", null, "3"}); result = gson.toJson(new String[] {"1", null, "3"});
assertThat(result).isEqualTo("[\"1\",null,\"3\"]"); assertThat(result).isEqualTo("[\"1\",null,\"3\"]");
} }
// test for issue 389 // test for issue 389
@Test @Test
public void testAbsentJsonElementsAreSetToNull() { public void testAbsentJsonElementsAreSetToNull() {
Gson gson = new Gson(); Gson gson = new Gson();
ClassWithInitializedMembers target = ClassWithInitializedMembers target =
gson.fromJson("{array:[1,2,3]}", ClassWithInitializedMembers.class); gson.fromJson("{array:[1,2,3]}", ClassWithInitializedMembers.class);
assertThat(target.array).hasLength(3); assertThat(target.array).hasLength(3);
assertThat(target.array[1]).isEqualTo(2); assertThat(target.array[1]).isEqualTo(2);
assertThat(target.str1).isEqualTo(ClassWithInitializedMembers.MY_STRING_DEFAULT); assertThat(target.str1).isEqualTo(ClassWithInitializedMembers.MY_STRING_DEFAULT);
assertThat(target.str2).isNull(); assertThat(target.str2).isNull();
assertThat(target.int1).isEqualTo(ClassWithInitializedMembers.MY_INT_DEFAULT); assertThat(target.int1).isEqualTo(ClassWithInitializedMembers.MY_INT_DEFAULT);
// test the default value of a primitive int field per JVM spec // test the default value of a primitive int field per JVM spec
assertThat(target.int2).isEqualTo(0); assertThat(target.int2).isEqualTo(0);
assertThat(target.bool1).isEqualTo(ClassWithInitializedMembers.MY_BOOLEAN_DEFAULT); assertThat(target.bool1).isEqualTo(ClassWithInitializedMembers.MY_BOOLEAN_DEFAULT);
// test the default value of a primitive boolean field per JVM spec // test the default value of a primitive boolean field per JVM spec
assertThat(target.bool2).isFalse(); assertThat(target.bool2).isFalse();
} }
public static class ClassWithInitializedMembers { public static class ClassWithInitializedMembers {
// Using a mix of no-args constructor and field initializers // Using a mix of no-args constructor and field initializers
// Also, some fields are intialized and some are not (so initialized per JVM spec) // Also, some fields are intialized and some are not (so initialized per JVM spec)
public static final String MY_STRING_DEFAULT = "string"; public static final String MY_STRING_DEFAULT = "string";
private static final int MY_INT_DEFAULT = 2; private static final int MY_INT_DEFAULT = 2;
private static final boolean MY_BOOLEAN_DEFAULT = true; private static final boolean MY_BOOLEAN_DEFAULT = true;
int[] array; int[] array;
String str1, str2; String str1, str2;
int int1 = MY_INT_DEFAULT; int int1 = MY_INT_DEFAULT;
int int2; int int2;
boolean bool1 = MY_BOOLEAN_DEFAULT; boolean bool1 = MY_BOOLEAN_DEFAULT;
boolean bool2; boolean bool2;
public ClassWithInitializedMembers() { public ClassWithInitializedMembers() {
str1 = MY_STRING_DEFAULT; str1 = MY_STRING_DEFAULT;
} }
} }
private static class ClassWithNullWrappedPrimitive { private static class ClassWithNullWrappedPrimitive {
private Long value; private Long value;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static class ClassWithMembers { private static class ClassWithMembers {
String str; String str;
int[] array; int[] array;
Collection<String> col; Collection<String> col;
} }
private static class ClassWithObjectsSerializer implements JsonSerializer<ClassWithObjects> { private static class ClassWithObjectsSerializer implements JsonSerializer<ClassWithObjects> {
@Override @Override
public JsonElement serialize( public JsonElement serialize(
ClassWithObjects src, Type typeOfSrc, JsonSerializationContext context) { ClassWithObjects src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.add("bag", JsonNull.INSTANCE); obj.add("bag", JsonNull.INSTANCE);
return obj; return obj;
} }
} }
@Test @Test
public void testExplicitNullSetsFieldToNullDuringDeserialization() { public void testExplicitNullSetsFieldToNullDuringDeserialization() {
Gson gson = new Gson(); Gson gson = new Gson();
String json = "{value:null}"; String json = "{value:null}";
ObjectWithField obj = gson.fromJson(json, ObjectWithField.class); ObjectWithField obj = gson.fromJson(json, ObjectWithField.class);
assertThat(obj.value).isNull(); assertThat(obj.value).isNull();
} }
@Test @Test
public void testCustomTypeAdapterPassesNullSerialization() { public void testCustomTypeAdapterPassesNullSerialization() {
Gson gson = Gson gson =
new GsonBuilder() new GsonBuilder()
.registerTypeAdapter( .registerTypeAdapter(
ObjectWithField.class, ObjectWithField.class,
new JsonSerializer<ObjectWithField>() { new JsonSerializer<ObjectWithField>() {
@Override @Override
public JsonElement serialize( public JsonElement serialize(
ObjectWithField src, Type typeOfSrc, JsonSerializationContext context) { ObjectWithField src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(null); return context.serialize(null);
} }
}) })
.create(); .create();
ObjectWithField target = new ObjectWithField(); ObjectWithField target = new ObjectWithField();
target.value = "value1"; target.value = "value1";
String json = gson.toJson(target); String json = gson.toJson(target);
assertThat(json).doesNotContain("value1"); assertThat(json).doesNotContain("value1");
} }
@Test @Test
public void testCustomTypeAdapterPassesNullDesrialization() { public void testCustomTypeAdapterPassesNullDesrialization() {
Gson gson = Gson gson =
new GsonBuilder() new GsonBuilder()
.registerTypeAdapter( .registerTypeAdapter(
ObjectWithField.class, ObjectWithField.class,
new JsonDeserializer<ObjectWithField>() { new JsonDeserializer<ObjectWithField>() {
@Override @Override
public ObjectWithField deserialize( public ObjectWithField deserialize(
JsonElement json, Type type, JsonDeserializationContext context) { JsonElement json, Type type, JsonDeserializationContext context) {
return context.deserialize(null, type); return context.deserialize(null, type);
} }
}) })
.create(); .create();
String json = "{value:'value1'}"; String json = "{value:'value1'}";
ObjectWithField target = gson.fromJson(json, ObjectWithField.class); ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
assertThat(target).isNull(); assertThat(target).isNull();
} }
private static class ObjectWithField { private static class ObjectWithField {
String value = ""; String value = "";
} }
} }

View File

@ -1,54 +1,54 @@
/* /*
* Copyright (C) 2015 Google Inc. * Copyright (C) 2015 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson.internal; package com.google.gson.internal;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.junit.Test; import org.junit.Test;
public class LazilyParsedNumberTest { public class LazilyParsedNumberTest {
@Test @Test
public void testHashCode() { public void testHashCode() {
LazilyParsedNumber n1 = new LazilyParsedNumber("1"); LazilyParsedNumber n1 = new LazilyParsedNumber("1");
LazilyParsedNumber n1Another = new LazilyParsedNumber("1"); LazilyParsedNumber n1Another = new LazilyParsedNumber("1");
assertThat(n1Another.hashCode()).isEqualTo(n1.hashCode()); assertThat(n1Another.hashCode()).isEqualTo(n1.hashCode());
} }
@Test @Test
public void testEquals() { public void testEquals() {
LazilyParsedNumber n1 = new LazilyParsedNumber("1"); LazilyParsedNumber n1 = new LazilyParsedNumber("1");
LazilyParsedNumber n1Another = new LazilyParsedNumber("1"); LazilyParsedNumber n1Another = new LazilyParsedNumber("1");
assertThat(n1.equals(n1Another)).isTrue(); assertThat(n1.equals(n1Another)).isTrue();
} }
@Test @Test
public void testJavaSerialization() throws IOException, ClassNotFoundException { public void testJavaSerialization() throws IOException, ClassNotFoundException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out); ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(new LazilyParsedNumber("123")); objOut.writeObject(new LazilyParsedNumber("123"));
objOut.close(); objOut.close();
ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())); ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
Number deserialized = (Number) objIn.readObject(); Number deserialized = (Number) objIn.readObject();
assertThat(deserialized).isEqualTo(new BigDecimal("123")); assertThat(deserialized).isEqualTo(new BigDecimal("123"));
} }
} }