diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 93cde7b55a..d04018591e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,3 +24,22 @@ jobs:
- name: Build with Maven
# This also runs javadoc:jar to detect any issues with the Javadoc generated during release
run: mvn --batch-mode --update-snapshots --no-transfer-progress verify javadoc:jar
+
+ native-image-test:
+ name: "GraalVM Native Image test"
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: "Set up GraalVM"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: '17'
+ distribution: 'graalvm'
+ # According to documentation in graalvm/setup-graalvm this is used to avoid rate-limiting issues
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ cache: 'maven'
+ - name: Build and run tests
+ # Only run tests in `graal-native-image-test` (and implicitly build and run tests in `gson`),
+ # everything else is covered already by regular build job above
+ run: mvn test --batch-mode --update-snapshots --no-transfer-progress --activate-profiles native-image-test --projects graal-native-image-test --also-make
diff --git a/.github/workflows/check-android-compatibility.yml b/.github/workflows/check-android-compatibility.yml
new file mode 100644
index 0000000000..e71956fa2c
--- /dev/null
+++ b/.github/workflows/check-android-compatibility.yml
@@ -0,0 +1,29 @@
+# For security reasons this is a separate GitHub workflow, see https://github.com/google/gson/issues/2429#issuecomment-1622522842
+# Once https://github.com/mojohaus/animal-sniffer/issues/252 or https://github.com/mojohaus/animal-sniffer/pull/253
+# are resolved, can consider adjusting pom.xml to include this as part of normal Maven build
+
+name: Check Android compatibility
+
+on: [push, pull_request]
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ check-android-compatibility:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'maven'
+
+ - name: Check Android compatibility
+ run: |
+ # Run 'test' phase because plugin normally expects to be executed after tests have been compiled
+ mvn --batch-mode --no-transfer-progress test animal-sniffer:check@check-android-compatibility -DskipTests
diff --git a/.github/workflows/check-api-compatibility.yml b/.github/workflows/check-api-compatibility.yml
index a446576408..79a793f564 100644
--- a/.github/workflows/check-api-compatibility.yml
+++ b/.github/workflows/check-api-compatibility.yml
@@ -1,3 +1,5 @@
+# This workflow makes sure that a pull request does not make any incompatible changes
+# to the public API of Gson
name: Check API compatibility
on: pull_request
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 01d95bdf6f..76822e60f4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -4,9 +4,9 @@ name: "CodeQL"
on:
push:
- branches: [ master ]
+ branches: [ main ]
pull_request:
- branches: [ master ]
+ branches: [ main ]
schedule:
# Run every Monday at 16:10
- cron: '10 16 * * 1'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b620adfdf..7efc0da9de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -188,7 +188,7 @@ _2015-10-04_
* New: APIs to add primitives directly to `JsonArray` instances.
* New: ISO 8601 date type adapter. Find this in _extras_.
* Fix: `FieldNamingPolicy` now works properly when running on a device with a Turkish locale.
- [autovalue]: https://github.com/google/auto/tree/master/value
+ [autovalue]: https://github.com/google/auto/tree/main/value
## Version 2.3.1
diff --git a/GsonDesignDocument.md b/GsonDesignDocument.md
index 2c3702ee13..a80a81e627 100644
--- a/GsonDesignDocument.md
+++ b/GsonDesignDocument.md
@@ -1,57 +1,59 @@
# Gson Design Document
-This document presents issues that we faced while designing Gson. It is meant for advanced users or developers working on Gson. If you are interested in learning how to use Gson, see its user guide.
+This document presents issues that we faced while designing Gson. It is meant for advanced users or developers working on Gson. If you are interested in learning how to use Gson, see its user guide.
-**Navigating the Json tree or the target Type Tree while deserializing**
+Some information in this document is outdated and does not reflect the current state of Gson. This information can however still be relevant for understanding the history of Gson.
-When you are deserializing a Json string into an object of desired type, you can either navigate the tree of the input, or the type tree of the desired type. Gson uses the latter approach of navigating the type of the target object. This keeps you in tight control of instantiating only the type of objects that you are expecting (essentially validating the input against the expected "schema"). By doing this, you also ignore any extra fields that the Json input has but were not expected.
+## Navigating the Json tree or the target Type Tree while deserializing
-As part of Gson, we wrote a general purpose ObjectNavigator that can take any object and navigate through its fields calling a visitor of your choice.
+When you are deserializing a Json string into an object of desired type, you can either navigate the tree of the input, or the type tree of the desired type. Gson uses the latter approach of navigating the type of the target object. This keeps you in tight control of instantiating only the type of objects that you are expecting (essentially validating the input against the expected "schema"). By doing this, you also ignore any extra fields that the Json input has but were not expected.
-**Supporting richer serialization semantics than deserialization semantics**
+As part of Gson, we wrote a general purpose ObjectNavigator that can take any object and navigate through its fields calling a visitor of your choice.
-Gson supports serialization of arbitrary collections, but can only deserialize genericized collections. this means that Gson can, in some cases, fail to deserialize Json that it wrote. This is primarily a limitation of the Java type system since when you encounter a Json array of arbitrary types there is no way to detect the types of individual elements. We could have chosen to restrict the serialization to support only generic collections, but chose not to.This is because often the user of the library are concerned with either serialization or deserialization, but not both. In such cases, there is no need to artificially restrict the serialization capabilities.
+## Supporting richer serialization semantics than deserialization semantics
-**Supporting serialization and deserialization of classes that are not under your control and hence can not be modified**
+Gson supports serialization of arbitrary collections, but can only deserialize genericized collections. this means that Gson can, in some cases, fail to deserialize Json that it wrote. This is primarily a limitation of the Java type system since when you encounter a Json array of arbitrary types there is no way to detect the types of individual elements. We could have chosen to restrict the serialization to support only generic collections, but chose not to. This is because often the user of the library are concerned with either serialization or deserialization, but not both. In such cases, there is no need to artificially restrict the serialization capabilities.
-Some Json libraries use annotations on fields or methods to indicate which fields should be used for Json serialization. That approach essentially precludes the use of classes from JDK or third-party libraries. We solved this problem by defining the notion of Custom serializers and deserializers. This approach is not new, and was used by the JAX-RPC technology to solve essentially the same problem.
+## Supporting serialization and deserialization of classes that are not under your control and hence can not be modified
-**Using Checked vs Unchecked exceptions to indicate a parsing error**
+Some Json libraries use annotations on fields or methods to indicate which fields should be used for Json serialization. That approach essentially precludes the use of classes from JDK or third-party libraries. We solved this problem by defining the notion of custom serializers and deserializers. This approach is not new, and was used by the JAX-RPC technology to solve essentially the same problem.
-We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the catch() block.
+## Using Checked vs Unchecked exceptions to indicate a parsing error
-**Creating class instances for deserialization**
+We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the `catch()` block.
-Gson needs to create a dummy class instance before it can deserialize Json data into its fields. We could have used Guice to get such an instance, but that would have resulted in a dependency on Guice. Moreover, it probably would have done the wrong thing since Guice is expected to return a valid instance, whereas we need to create a dummy one. Worse, Gson would overwrite the fields of that instance with the incoming data there by modifying the instance for all subsequent Guice injections. This is clearly not a desired behavior. Hence, we create class instances by invoking the parameterless constructor. We also handle the primitive types, enums, collections, sets, maps and trees as a special case.
+## Creating class instances for deserialization
-To solve the problem of supporting unmodifiable types, we use custom instance creators. So, if you want to use a library types that does not define a default constructor (for example, Money class), then you can register an instance creator that returns a dummy instance when asked.
+Gson needs to create a dummy class instance before it can deserialize Json data into its fields. We could have used Guice to get such an instance, but that would have resulted in a dependency on Guice. Moreover, it probably would have done the wrong thing since Guice is expected to return a valid instance, whereas we need to create a dummy one. Worse, Gson would overwrite the fields of that instance with the incoming data thereby modifying the instance for all subsequent Guice injections. This is clearly not a desired behavior. Hence, we create class instances by invoking the parameterless constructor. We also handle the primitive types, enums, collections, sets, maps and trees as a special case.
-**Using fields vs getters to indicate Json elements**
+To solve the problem of supporting unmodifiable types, we use custom instance creators. So, if you want to use a library type that does not define a default constructor (for example, `Money` class), then you can register an instance creator that returns a dummy instance when asked.
-Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, getXXX or isXXX might be semantic rather than indicating properties.
+## Using fields vs getters to indicate Json elements
-However, there are good arguments to support properties as well. We intend to enhance Gson in a latter version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
+Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, `getXXX` or `isXXX` might be semantic rather than indicating properties.
-**Why are most classes in Gson marked as final?**
+However, there are good arguments to support properties as well. We intend to enhance Gson in a later version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
-While Gson provides a fairly extensible architecture by providing pluggable serializers and deserializers, Gson classes were not specifically designed to be extensible. Providing non-final classes would have allowed a user to legitimately extend Gson classes, and then expect that behavior to work in all subsequent revisions. We chose to limit such use-cases by marking classes as final, and waiting until a good use-case emerges to allow extensibility. Marking a class final also has a minor benefit of providing additional optimization opportunities to Java compiler and virtual machine.
+## Why are most classes in Gson marked as final?
-**Why are inner interfaces and classes used heavily in Gson?**
+While Gson provides a fairly extensible architecture by providing pluggable serializers and deserializers, Gson classes were not specifically designed to be extensible. Providing non-final classes would have allowed a user to legitimately extend Gson classes, and then expect that behavior to work in all subsequent revisions. We chose to limit such use-cases by marking classes as final, and waiting until a good use-case emerges to allow extensibility. Marking a class final also has a minor benefit of providing additional optimization opportunities to Java compiler and virtual machine.
-Gson uses inner classes substantially. Many of the public interfaces are inner interfaces too (see JsonSerializer.Context or JsonDeserializer.Context as an example). These are primarily done as a matter of style. For example, we could have moved JsonSerializer.Context to be a top-level class JsonSerializerContext, but chose not to do so. However, if you can give us good reasons to rename it alternately, we are open to changing this philosophy.
+## Why are inner interfaces and classes used heavily in Gson?
-**Why do you provide two ways of constructing Gson?**
+Gson uses inner classes substantially. Many of the public interfaces are inner interfaces too (see `JsonSerializer.Context` or `JsonDeserializer.Context` as an example). These are primarily done as a matter of style. For example, we could have moved `JsonSerializer.Context` to be a top-level class `JsonSerializerContext`, but chose not to do so. However, if you can give us good reasons to rename it alternately, we are open to changing this philosophy.
-Gson can be constructed in two ways: by invoking new Gson() or by using a GsonBuilder. We chose to provide a simple no-args constructor to handle simple use-cases for Gson where you want to use default options, and quickly want to get going with writing code. For all other situations, where you need to configure Gson with options such as formatters, version controls etc, we use a builder pattern. The builder pattern allows a user to specify multiple optional settings for what essentially become constructor parameters for Gson.
+## Why do you provide two ways of constructing Gson?
-**Comparing Gson with Alternate Approaches**
+Gson can be constructed in two ways: by invoking `new Gson()` or by using a `GsonBuilder`. We chose to provide a simple no-args constructor to handle simple use-cases for Gson where you want to use default options, and quickly want to get going with writing code. For all other situations, where you need to configure Gson with options such as formatters, version controls etc., we use a builder pattern. The builder pattern allows a user to specify multiple optional settings for what essentially become constructor parameters for Gson.
+
+## Comparing Gson with alternate approaches
Note that these comparisons were done while developing Gson so these date back to mid to late 2007.
-__Comparing Gson with org.json library__
+### Comparing Gson with org.json library
-org.json is a much lower-level library that can be used to write a toJson() method in a class. If you can not use Gson directly (may be because of platform restrictions regarding reflection), you could use org.json to hand-code a toJson method in each object.
+org.json is a much lower-level library that can be used to write a `toJson()` method in a class. If you can not use Gson directly (maybe because of platform restrictions regarding reflection), you could use org.json to hand-code a `toJson` method in each object.
-__Comparing Gson with org.json.simple library__
+### Comparing Gson with org.json.simple library
org.json.simple library is very similar to org.json library and hence fairly low level. The key issue with this library is that it does not handle exceptions very well. In some cases it appeared to just eat the exception while in other cases it throws an "Error" rather than an exception.
diff --git a/README.md b/README.md
index 04eb1c94e8..7418a27af7 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,13 @@ When this module is present, Gson can use the `Unsafe` class to create instances
However, care should be taken when relying on this. `Unsafe` is not available in all environments and its usage has some pitfalls,
see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()).
+#### Minimum Android API level
+
+- Gson 2.11.0 and newer: API level 21
+- Gson 2.10.1 and older: API level 19
+
+Older Gson versions may also support lower API levels, however this has not been verified.
+
### Documentation
* [API Javadoc](https://www.javadoc.io/doc/com.google.code.gson/gson): Documentation for the current release
* [User guide](UserGuide.md): This guide contains examples on how to use Gson in your code
diff --git a/Troubleshooting.md b/Troubleshooting.md
index 2f9185a38e..91fd22110a 100644
--- a/Troubleshooting.md
+++ b/Troubleshooting.md
@@ -2,7 +2,10 @@
This guide describes how to troubleshoot common issues when using Gson.
-## `ClassCastException` when using deserialized object
+
+
+
+## `ClassCastException` when using deserialized object
**Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson
@@ -14,9 +17,9 @@ This guide describes how to troubleshoot common issues when using Gson.
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)).
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>()` (where `T` is a type variable). Due to Java type erasure the actual type of `T` is not available at runtime. Refactor your code to pass around `TypeToken` instances or use [`TypeToken.getParameterized(...)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/reflect/TypeToken.html#getParameterized(java.lang.reflect.Type,java.lang.reflect.Type...)), for example `TypeToken.getParameterized(List.class, elementClass)`.
+- When using `TypeToken` make sure you don't capture a type variable. For example avoid something like `new TypeToken>()` (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.
-## `InaccessibleObjectException`: 'module ... does not "opens ..." to unnamed module'
+## `InaccessibleObjectException`: 'module ... does not "opens ..." to unnamed module'
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to unnamed module' is thrown
@@ -30,7 +33,7 @@ When no built-in adapter for a type exists and no custom adapter has been regist
If you want to prevent using reflection on third-party classes in the future you can write your own [`ReflectionAccessFilter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/ReflectionAccessFilter.html) or use one of the predefined ones, such as `ReflectionAccessFilter.BLOCK_ALL_PLATFORM`.
-## `InaccessibleObjectException`: 'module ... does not "opens ..." to module com.google.gson'
+## `InaccessibleObjectException`: 'module ... does not "opens ..." to module com.google.gson'
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to module com.google.gson' is thrown
@@ -51,7 +54,7 @@ module mymodule {
Or in case this occurs for a field in one of your classes which you did not actually want to serialize or deserialize in the first place, you can exclude that field, see the [user guide](UserGuide.md#excluding-fields-from-serialization-and-deserialization).
-## Android app not working in Release mode; random property names
+## Android app not working in Release mode; random property names
**Symptom:** Your Android app is working fine in Debug mode but fails in Release mode and the JSON properties have seemingly random names such as `a`, `b`, ...
@@ -59,11 +62,11 @@ Or in case this occurs for a field in one of your classes which you did not actu
**Solution:** Make sure you have configured ProGuard / R8 correctly to preserve the names of your fields. See the [Android example](examples/android-proguard-example/README.md) for more information.
-## Android app unable to parse JSON after app update
+## 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
-**Reason:** You probably have not configured ProGuard / R8 correctly; probably the fields 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.
@@ -71,7 +74,7 @@ If you want to preserve backward compatibility for you app you can use [`@Serial
Normally ProGuard and R8 produce a mapping file, this makes it easier to find out the obfuscated field names instead of having to find them out through trial and error or other means. See the [Android Studio user guide](https://developer.android.com/studio/build/shrink-code.html#retracing) for more information.
-## Default field values not present after deserialization
+## Default field values not present after deserialization
**Symptom:** You have assign default values to fields but after deserialization the fields have their standard value (such as `null` or `0`)
@@ -84,7 +87,7 @@ Normally ProGuard and R8 produce a mapping file, this makes it easier to find ou
Otherwise Gson will by default try to use JDK `Unsafe` or similar means to create an instance of your class without invoking the constructor and without running any initializers. You can also disable that behavior through [`GsonBuilder.disableJdkUnsafe()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()) to notice such issues early on.
-## `null` values for anonymous and local classes
+## `null` values for anonymous and local classes
**Symptom:** Objects of a class are always serialized as JSON `null` / always deserialized as Java `null`
@@ -97,7 +100,7 @@ Notes:
- "double brace-initialization" also creates anonymous classes
- Local record classes (feature added in Java 16) are supported by Gson and are not affected by this
-## Map keys having unexpected format in JSON
+## Map keys having unexpected format in JSON
**Symptom:** JSON output for `Map` keys is unexpected / cannot be deserialized again
@@ -105,33 +108,53 @@ Notes:
**Solution:** Use [`GsonBuilder.enableComplexMapKeySerialization()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#enableComplexMapKeySerialization()). See also the [user guide](UserGuide.md#maps-examples) for more information.
-## Parsing JSON fails with `MalformedJsonException`
+## Parsing JSON fails with `MalformedJsonException`
**Symptom:** JSON parsing fails with `MalformedJsonException`
**Reason:** The JSON data is actually malformed
-**Solution:** During debugging log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Sometimes APIs might return HTML error pages (instead of JSON data) when reaching rate limits or when other errors occur. Also read the location information of the `MalformedJsonException` exception message, it indicates where exactly in the document the malformed data was detected, including the [JSONPath](https://goessner.net/articles/JsonPath/).
+**Solution:** During debugging, log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Sometimes APIs might return HTML error pages (instead of JSON data) when reaching rate limits or when other errors occur. Also read the location information of the `MalformedJsonException` exception message, it indicates where exactly in the document the malformed data was detected, including the [JSONPath](https://goessner.net/articles/JsonPath/).
+
+For example, let's assume you want to deserialize the following JSON data:
+
+```json
+{
+ "languages": [
+ "English",
+ "French",
+ ]
+}
+```
-## Integral JSON number is parsed as `double`
+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 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.
+
+## Integral JSON number is parsed 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 create 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
-## Malformed JSON not rejected
+## Malformed JSON not rejected
**Symptom:** Gson parses malformed JSON without throwing any exceptions
**Reason:** Due to legacy reasons Gson performs parsing by default in lenient mode
-**Solution:** See [`Gson` class documentation](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/Gson.html) section "Lenient JSON handling"
+**Solution:** 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))
+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.
+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.
-Note: Even in non-lenient mode Gson deviates slightly from the JSON specification, see [`JsonReader.setLenient`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/stream/JsonReader.html#setLenient(boolean)) for more details.
-
-## `IllegalStateException`: "Expected ... but was ..."
+## `IllegalStateException`: "Expected ... but was ..."
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was ..." is thrown
@@ -139,13 +162,36 @@ Note: Even in non-lenient mode Gson deviates slightly from the JSON specificatio
**Solution:** Make sure that your classes correctly model the JSON data. Also during debugging log the JSON data right before calling Gson methods or set a breakpoint to inspect the data and make sure it has the expected format. Read the location information of the exception message, it indicates where exactly in the document the error occurred, including the [JSONPath](https://goessner.net/articles/JsonPath/).
-## `IllegalStateException`: "Expected ... but was NULL"
+For example, let's assume you have the following Java class:
+
+```java
+class WebPage {
+ String languages;
+}
+```
+
+And you want to deserialize the following JSON data:
+
+```json
+{
+ "languages": ["English", "French"]
+}
+```
+
+This will fail with an exception similar to this one: `IllegalStateException: Expected a string but was BEGIN_ARRAY at line 2 column 17 path $.languages`
+This means Gson expected a JSON string value but found the beginning of a JSON array (`[`). The location information "line 2 column 17" points to the `[` in the JSON data (with some slight inaccuracies), so does the JSONPath `$.languages` in the exception message. It refers to the `languages` member of the root object (`$.`).
+The solution here is to change in the `WebPage` class the field `String languages` to `List languages`.
+
+## `IllegalStateException`: "Expected ... but was NULL"
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was NULL" is thrown
-**Reason:** You have written a custom `TypeAdapter` which does not properly handle a JSON null value
+**Reason:**
+
+- A built-in adapter does not support JSON null values
+- You have written a custom `TypeAdapter` which does not properly handle JSON null values
-**Solution:** Add code similar to the following at the beginning of the `read` method of your adapter:
+**Solution:** If this occurs for a custom adapter you wrote, add code similar to the following at the beginning of its `read` method:
```java
@Override
@@ -154,14 +200,14 @@ public MyClass read(JsonReader in) throws IOException {
in.nextNull();
return null;
}
-
+
...
}
```
Alternatively you can call [`nullSafe()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html#nullSafe()) on the adapter instance you created.
-## Properties missing in JSON
+## Properties missing in JSON
**Symptom:** Properties are missing in the JSON output
@@ -171,7 +217,7 @@ Alternatively you can call [`nullSafe()`](https://www.javadoc.io/doc/com.google.
Note: Gson does not support anonymous and local classes and will serialize them as JSON null, see the [related troubleshooting point](#null-values-for-anonymous-and-local-classes).
-## JSON output changes for newer Android versions
+## JSON output changes for newer Android versions
**Symptom:** The JSON output differs when running on newer Android versions
@@ -185,7 +231,7 @@ When no built-in adapter for a type exists and no custom adapter has been regist
If you want to prevent using reflection on third-party classes in the future you can write your own [`ReflectionAccessFilter`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/ReflectionAccessFilter.html) or use one of the predefined ones, such as `ReflectionAccessFilter.BLOCK_ALL_PLATFORM`.
-## JSON output contains values of `static` fields
+## JSON output contains values of `static` fields
**Symptom:** The JSON output contains values of `static` fields
@@ -193,7 +239,7 @@ If you want to prevent using reflection on third-party classes in the future you
**Solution:** When calling `GsonBuilder.excludeFieldsWithModifiers` you overwrite the default excluded modifiers. Therefore, you have to explicitly exclude `static` fields if desired. This can be done by adding `Modifier.STATIC` as additional argument.
-## `NoSuchMethodError` when calling Gson methods
+## `NoSuchMethodError` when calling Gson methods
**Symptom:** A `java.lang.NoSuchMethodError` is thrown when trying to call certain Gson methods
@@ -210,3 +256,102 @@ System.out.println(Gson.class.getProtectionDomain().getCodeSource().getLocation(
```
If that fails with a `NullPointerException` you have to try one of the other ways to find out where a class is loaded from.
+
+## `IllegalArgumentException`: 'Class ... declares multiple JSON fields named '...''
+
+**Symptom:** An exception with the message 'Class ... declares multiple JSON fields named '...'' is thrown
+
+**Reason:**
+
+- The name you have specified with a [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) annotation for a field collides with the name of another field
+- The [`FieldNamingStrategy`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/FieldNamingStrategy.html) you have specified produces conflicting field names
+- A field of your class has the same name as the field of a superclass
+
+Gson prevents multiple fields with the same name because during deserialization it would be ambiguous for which field the JSON data should be deserialized. For serialization it would cause the same field to appear multiple times in JSON. While the JSON specification permits this, it is likely that the application parsing the JSON data will not handle it correctly.
+
+**Solution:** First identify the fields with conflicting names based on the exception message. Then decide if you want to rename one of them using the [`@SerializedName`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) annotation, or if you want to [exclude](UserGuide.md#excluding-fields-from-serialization-and-deserialization) one of them. When excluding one of the fields you have to include it for both serialization and deserialization (even if your application only performs one of these actions) because the duplicate field check cannot differentiate between these actions.
+
+## `UnsupportedOperationException` when serializing or deserializing `java.lang.Class`
+
+**Symptom:** An `UnsupportedOperationException` is thrown when trying to serialize or deserialize `java.lang.Class`
+
+**Reason:** Gson intentionally does not permit serializing and deserializing `java.lang.Class` for security reasons. Otherwise a malicious user could make your application load an arbitrary class from the classpath and, depending on what your application does with the `Class`, in the worst case perform a remote code execution attack.
+
+**Solution:** First check if you really need to serialize or deserialize a `Class`. Often it is possible to use string aliases and then map them to the known `Class`; you could write a custom [`TypeAdapter`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/TypeAdapter.html) to do this. If the `Class` values are not known in advance, try to introduce a common base class or interface for all these classes and then verify that the deserialized class is a subclass. For example assuming the base class is called `MyBaseClass`, your custom `TypeAdapter` should load the class like this:
+
+```java
+Class.forName(jsonString, false, getClass().getClassLoader()).asSubclass(MyBaseClass.class)
+```
+
+This will not initialize arbitrary classes, and it will throw a `ClassCastException` if the loaded class is not the same as or a subclass of `MyBaseClass`.
+
+## `IllegalStateException`: 'TypeToken must be created with a type argument' `RuntimeException`: 'Missing type parameter'
+
+**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.
+
+**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>() {}`. 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.
+
+**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
+-keepattributes Signature
+
+# Keep class TypeToken (respectively its generic signature)
+-keep class com.google.gson.reflect.TypeToken { *; }
+
+# Keep any (anonymous) classes extending TypeToken
+-keep class * extends com.google.gson.reflect.TypeToken
+```
+
+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.
+
+## `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).
+
+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.
+
+**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
+-keepclassmembers class com.example.MyClass {
+ ();
+}
+```
+
+You can also use `(...);` 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 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.
+
+## `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
+
+**Reason:** This exception is thrown when you create an anonymous `TypeToken` subclass which captures a type variable, for example `new TypeToken>() {}` (where `T` is a type variable). At compile time such code looks safe and you can use the type `List` 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