Skip to content

Commit f2f53fb

Browse files
authored
Add troubleshooting guide (#2285)
1 parent f63a1b8 commit f2f53fb

File tree

4 files changed

+199
-6
lines changed

4 files changed

+199
-6
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: Bug report
3-
about: Report a Gson bug.
3+
about: Report a Gson bug. Please have a look at the troubleshooting guide (Troubleshooting.md) first.
44
title: ''
55
labels: bug
66
assignees: ''

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gs
5757

5858
### Documentation
5959
* [API Javadoc](https://www.javadoc.io/doc/com.google.code.gson/gson): Documentation for the current release
60-
* [User guide](https://github.com/google/gson/blob/master/UserGuide.md): This guide contains examples on how to use Gson in your code.
61-
* [Change log](https://github.com/google/gson/blob/master/CHANGELOG.md): Changes in the recent versions
62-
* [Design document](https://github.com/google/gson/blob/master/GsonDesignDocument.md): This document discusses issues we faced while designing Gson. It also includes a comparison of Gson with other Java libraries that can be used for Json conversion
60+
* [User guide](UserGuide.md): This guide contains examples on how to use Gson in your code
61+
* [Troubleshooting guide](Troubleshooting.md): Describes how to solve common issues when using Gson
62+
* [Change log](CHANGELOG.md): Changes in the recent versions
63+
* [Design document](GsonDesignDocument.md): This document discusses issues we faced while designing Gson. It also includes a comparison of Gson with other Java libraries that can be used for Json conversion
6364

6465
Please use the ['gson' tag on StackOverflow](https://stackoverflow.com/questions/tagged/gson) or the [google-gson Google group](https://groups.google.com/group/google-gson) to discuss Gson or to post questions.
6566

Troubleshooting.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Troubleshooting Guide
2+
3+
This guide describes how to troubleshoot common issues when using Gson.
4+
5+
## `ClassCastException` when using deserialized object
6+
7+
**Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson
8+
9+
**Reason:** Your code is most likely not type-safe
10+
11+
**Solution:** Make sure your code adheres to the following:
12+
13+
- Avoid raw types: Instead of calling `fromJson(..., List.class)`, create for example a `TypeToken<List<MyClass>>`.
14+
See the [user guide](UserGuide.md#TOC-Collections-Examples) for more information.
15+
- 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)).
16+
The overloads with `Type` parameter do not provide any type-safety guarantees.
17+
- When using `TypeToken` make sure you don't capture a type variable. For example avoid something like `new TypeToken<List<T>>()` (where `T` is a type variable). Due to Java type erasure the actual type of `T` is not available at runtime. Refactor your code to pass around `TypeToken` instances or use [`TypeToken.getParameterized(...)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/reflect/TypeToken.html#getParameterized(java.lang.reflect.Type,java.lang.reflect.Type...)), for example `TypeToken.getParameterized(List.class, elementClass)`.
18+
19+
## `InaccessibleObjectException`: 'module ... does not "opens ..." to unnamed module'
20+
21+
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to unnamed module' is thrown
22+
23+
**Reason:** You use Gson by accident to access internal fields of third-party classes
24+
25+
**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
26+
27+
**Explanation:**
28+
29+
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).
30+
31+
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`.
32+
33+
## `InaccessibleObjectException`: 'module ... does not "opens ..." to module com.google.gson'
34+
35+
**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to module com.google.gson' is thrown
36+
37+
**Reason:**
38+
39+
- 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.
40+
- 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).
41+
42+
**Solution:** Make sure the `module-info.java` file of your project allows Gson to use reflection on your classes, for example:
43+
44+
```java
45+
module mymodule {
46+
requires com.google.gson;
47+
48+
opens mypackage to com.google.gson;
49+
}
50+
```
51+
52+
## Android app not working in Release mode; random property names
53+
54+
**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`, ...
55+
56+
**Reason:** You probably have not configured ProGuard / R8 correctly
57+
58+
**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.
59+
60+
## Android app unable to parse JSON after app update
61+
62+
**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
63+
64+
**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
65+
66+
**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.
67+
68+
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")`
69+
70+
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.
71+
72+
## Default field values not present after deserialization
73+
74+
**Symptom:** You have assign default values to fields but after deserialization the fields have their standard value (such as `null` or `0`)
75+
76+
**Reason:** Gson cannot invoke the constructor of your class and falls back to JDK `Unsafe` (or similar means)
77+
78+
**Solution:** Make sure that the class:
79+
80+
- is `static` (explicitly or implicitly when it is a top-level class)
81+
- has a no-args constructor
82+
83+
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.
84+
85+
## `null` values for anonymous and local classes
86+
87+
**Symptom:** Objects of a class are always serialized as JSON `null` / always deserialized as Java `null`
88+
89+
**Reason:** The class you are serializing or deserializing is an anonymous or a local class (or you have specified a custom `ExclusionStrategy`)
90+
91+
**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.
92+
93+
Notes:
94+
95+
- "double brace-initialization" also creates anonymous classes
96+
- Local record classes (feature added in Java 16) are supported by Gson and are not affected by this
97+
98+
## Map keys having unexpected format in JSON
99+
100+
**Symptom:** JSON output for `Map` keys is unexpected / cannot be deserialized again
101+
102+
**Reason:** The `Map` key type is 'complex' and you have not configured the `GsonBuilder` properly
103+
104+
**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#TOC-Maps-Examples) for more information.
105+
106+
## Parsing JSON fails with `MalformedJsonException`
107+
108+
**Symptom:** JSON parsing fails with `MalformedJsonException`
109+
110+
**Reason:** The JSON data is actually malformed
111+
112+
**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/).
113+
114+
## Integral JSON number is parsed as `double`
115+
116+
**Symptom:** JSON data contains an integral number such as `45` but Gson returns it as `double`
117+
118+
**Reason:** When parsing a JSON number as `Object`, Gson will by default create always return a `double`
119+
120+
**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
121+
122+
## Malformed JSON not rejected
123+
124+
**Symptom:** Gson parses malformed JSON without throwing any exceptions
125+
126+
**Reason:** Due to legacy reasons Gson performs parsing by default in lenient mode
127+
128+
**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"
129+
130+
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.
131+
132+
## `IllegalStateException`: "Expected ... but was ..."
133+
134+
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was ..." is thrown
135+
136+
**Reason:** The JSON data does not have the correct format
137+
138+
**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/).
139+
140+
## `IllegalStateException`: "Expected ... but was NULL"
141+
142+
**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was NULL" is thrown
143+
144+
**Reason:** You have written a custom `TypeAdapter` which does not properly handle a JSON null value
145+
146+
**Solution:** Add code similar to the following at the beginning of the `read` method of your adapter:
147+
148+
```java
149+
@Override
150+
public MyClass read(JsonReader in) throws IOException {
151+
if (in.peek() == JsonToken.NULL) {
152+
in.nextNull();
153+
return null;
154+
}
155+
156+
...
157+
}
158+
```
159+
160+
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.
161+
162+
## Properties missing in JSON
163+
164+
**Symptom:** Properties are missing in the JSON output
165+
166+
**Reason:** Gson by default omits JSON null from the output (or: ProGuard / R8 is not configured correctly and removed unused fields)
167+
168+
**Solution:** Use [`GsonBuilder.serializeNulls()`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#serializeNulls())
169+
170+
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).
171+
172+
## JSON output changes for newer Android versions
173+
174+
**Symptom:** The JSON output differs when running on newer Android versions
175+
176+
**Reason:** You use Gson by accident to access internal fields of Android classes
177+
178+
**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
179+
180+
**Explanation:**
181+
182+
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.
183+
184+
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`.
185+
186+
## JSON output contains values of `static` fields
187+
188+
**Symptom:** The JSON output contains values of `static` fields
189+
190+
**Reason:** You used `GsonBuilder.excludeFieldsWithModifiers` to overwrite the default excluded modifiers
191+
192+
**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` to the argument.

gson/src/main/java/com/google/gson/stream/JsonReader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,7 +1547,7 @@ private String getPath(boolean usePreviousPath) {
15471547
}
15481548

15491549
/**
1550-
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
1550+
* Returns a <a href="https://goessner.net/articles/JsonPath/">JSONPath</a>
15511551
* in <i>dot-notation</i> to the previous (or current) location in the JSON document:
15521552
* <ul>
15531553
* <li>For JSON arrays the path points to the index of the previous element.<br>
@@ -1564,7 +1564,7 @@ public String getPreviousPath() {
15641564
}
15651565

15661566
/**
1567-
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
1567+
* Returns a <a href="https://goessner.net/articles/JsonPath/">JSONPath</a>
15681568
* in <i>dot-notation</i> to the next (or current) location in the JSON document:
15691569
* <ul>
15701570
* <li>For JSON arrays the path points to the index of the next element (even

0 commit comments

Comments
 (0)