diff --git a/README.md b/README.md index 2037d405..4ee9f76e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ There are a few open-source projects that can convert Java objects to JSON. Howe Gradle: ```gradle dependencies { - implementation 'com.google.code.gson:gson:2.10' + implementation 'com.google.code.gson:gson:2.10.1' } ``` @@ -54,7 +54,7 @@ Maven: com.google.code.gson gson - 2.10 + 2.10.1 ``` @@ -83,9 +83,10 @@ see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gs ### Documentation * [API Javadoc](https://www.javadoc.io/doc/com.google.code.gson/gson): Documentation for the current release - * [User guide](https://github.com/google/gson/blob/master/UserGuide.md): This guide contains examples on how to use Gson in your code. - * [Change log](https://github.com/google/gson/blob/master/CHANGELOG.md): Changes in the recent versions - * [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 + * [User guide](UserGuide.md): This guide contains examples on how to use Gson in your code + * [Troubleshooting guide](Troubleshooting.md): Describes how to solve common issues when using Gson + * [Change log](CHANGELOG.md): Changes in the recent versions + * [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 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. diff --git a/Troubleshooting.md b/Troubleshooting.md new file mode 100644 index 00000000..2f9185a3 --- /dev/null +++ b/Troubleshooting.md @@ -0,0 +1,212 @@ +# Troubleshooting Guide + +This guide describes how to troubleshoot common issues when using Gson. + +## `ClassCastException` when using deserialized object + +**Symptom:** `ClassCastException` is thrown when accessing an object deserialized by Gson + +**Reason:** Your code is most likely not type-safe + +**Solution:** Make sure your code adheres to the following: + +- Avoid raw types: Instead of calling `fromJson(..., List.class)`, create for example a `TypeToken>`. + 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)`. + +## `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 + +**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). + +**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). + +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' + +**Symptom:** An exception with a message in the form 'module ... does not "opens ..." to module com.google.gson' is thrown + +**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 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: + +```java +module mymodule { + requires 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). + +## 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`, ... + +**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. + +## 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 + +**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")` + +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 + +**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) + +**Solution:** Make sure that the class: + +- is `static` (explicitly or implicitly when it is a top-level class) +- 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. + +## `null` values for anonymous and local classes + +**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`) + +**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: + +- "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 + +**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 + +**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` + +**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/). + +## 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` + +**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 + +**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" + +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 ..." + +**Symptom:** An `IllegalStateException` with a message in the form "Expected ... but was ..." is thrown + +**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/). + +## `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 + +**Solution:** Add code similar to the following at the beginning of the `read` method of your adapter: + +```java +@Override +public MyClass read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + 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 + +**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) + +**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). + +## JSON output changes for 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 + +**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:** + +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`. + +## 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 + +**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 + +**Symptom:** A `java.lang.NoSuchMethodError` is thrown when trying to call certain Gson methods + +**Reason:** + +- 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, 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: + +```java +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. diff --git a/UserGuide.md b/UserGuide.md index b82bd725..05380671 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -1,48 +1,48 @@ # Gson User Guide -1. [Overview](#TOC-Overview) -2. [Goals for Gson](#TOC-Goals-for-Gson) -3. [Gson Performance and Scalability](#TOC-Gson-Performance-and-Scalability) -4. [Gson Users](#TOC-Gson-Users) -5. [Using Gson](#TOC-Using-Gson) - * [Using Gson with Gradle/Android](#TOC-Gson-With-Gradle) - * [Using Gson with Maven](#TOC-Gson-With-Maven) - * [Primitives Examples](#TOC-Primitives-Examples) - * [Object Examples](#TOC-Object-Examples) - * [Finer Points with Objects](#TOC-Finer-Points-with-Objects) - * [Nested Classes (including Inner Classes)](#TOC-Nested-Classes-including-Inner-Classes-) - * [Array Examples](#TOC-Array-Examples) - * [Collections Examples](#TOC-Collections-Examples) - * [Collections Limitations](#TOC-Collections-Limitations) - * [Maps Examples](#TOC-Maps-Examples) - * [Serializing and Deserializing Generic Types](#TOC-Serializing-and-Deserializing-Generic-Types) - * [Serializing and Deserializing Collection with Objects of Arbitrary Types](#TOC-Serializing-and-Deserializing-Collection-with-Objects-of-Arbitrary-Types) - * [Built-in Serializers and Deserializers](#TOC-Built-in-Serializers-and-Deserializers) - * [Custom Serialization and Deserialization](#TOC-Custom-Serialization-and-Deserialization) - * [Writing a Serializer](#TOC-Writing-a-Serializer) - * [Writing a Deserializer](#TOC-Writing-a-Deserializer) - * [Writing an Instance Creator](#TOC-Writing-an-Instance-Creator) - * [InstanceCreator for a Parameterized Type](#TOC-InstanceCreator-for-a-Parameterized-Type) - * [Compact Vs. Pretty Printing for JSON Output Format](#TOC-Compact-Vs.-Pretty-Printing-for-JSON-Output-Format) - * [Null Object Support](#TOC-Null-Object-Support) - * [Versioning Support](#TOC-Versioning-Support) - * [Excluding Fields From Serialization and Deserialization](#TOC-Excluding-Fields-From-Serialization-and-Deserialization) - * [Java Modifier Exclusion](#TOC-Java-Modifier-Exclusion) - * [Gson's `@Expose`](#TOC-Gson-s-Expose) - * [User Defined Exclusion Strategies](#TOC-User-Defined-Exclusion-Strategies) - * [JSON Field Naming Support](#TOC-JSON-Field-Naming-Support) - * [Sharing State Across Custom Serializers and Deserializers](#TOC-Sharing-State-Across-Custom-Serializers-and-Deserializers) - * [Streaming](#TOC-Streaming) -6. [Issues in Designing Gson](#TOC-Issues-in-Designing-Gson) -7. [Future Enhancements to Gson](#TOC-Future-Enhancements-to-Gson) +1. [Overview](#overview) +2. [Goals for Gson](#goals-for-gson) +3. [Gson Performance and Scalability](#gson-performance-and-scalability) +4. [Gson Users](#gson-users) +5. [Using Gson](#using-gson) + * [Using Gson with Gradle/Android](#using-gson-with-gradleandroid) + * [Using Gson with Maven](#using-gson-with-maven) + * [Primitives Examples](#primitives-examples) + * [Object Examples](#object-examples) + * [Finer Points with Objects](#finer-points-with-objects) + * [Nested Classes (including Inner Classes)](#nested-classes-including-inner-classes) + * [Array Examples](#array-examples) + * [Collections Examples](#collections-examples) + * [Collections Limitations](#collections-limitations) + * [Maps Examples](#maps-examples) + * [Serializing and Deserializing Generic Types](#serializing-and-deserializing-generic-types) + * [Serializing and Deserializing Collection with Objects of Arbitrary Types](#serializing-and-deserializing-collection-with-objects-of-arbitrary-types) + * [Built-in Serializers and Deserializers](#built-in-serializers-and-deserializers) + * [Custom Serialization and Deserialization](#custom-serialization-and-deserialization) + * [Writing a Serializer](#writing-a-serializer) + * [Writing a Deserializer](#writing-a-deserializer) + * [Writing an Instance Creator](#writing-an-instance-creator) + * [InstanceCreator for a Parameterized Type](#instancecreator-for-a-parameterized-type) + * [Compact Vs. Pretty Printing for JSON Output Format](#compact-vs-pretty-printing-for-json-output-format) + * [Null Object Support](#null-object-support) + * [Versioning Support](#versioning-support) + * [Excluding Fields From Serialization and Deserialization](#excluding-fields-from-serialization-and-deserialization) + * [Java Modifier Exclusion](#java-modifier-exclusion) + * [Gson's `@Expose`](#gsons-expose) + * [User Defined Exclusion Strategies](#user-defined-exclusion-strategies) + * [JSON Field Naming Support](#json-field-naming-support) + * [Sharing State Across Custom Serializers and Deserializers](#sharing-state-across-custom-serializers-and-deserializers) + * [Streaming](#streaming) +6. [Issues in Designing Gson](#issues-in-designing-gson) +7. [Future Enhancements to Gson](#future-enhancements-to-gson) -## Overview +## Overview Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source code of. -## Goals for Gson +## Goals for Gson * Provide easy to use mechanisms like `toString()` and constructor (factory method) to convert Java to JSON and vice-versa * Allow pre-existing unmodifiable objects to be converted to and from JSON @@ -50,7 +50,7 @@ Gson can work with arbitrary Java objects including pre-existing objects that yo * Support arbitrarily complex objects * Generate compact and readable JSON output -## Gson Performance and Scalability +## Gson Performance and Scalability Here are some metrics that we obtained on a desktop (dual opteron, 8GB RAM, 64-bit Ubuntu) running lots of other things along-with the tests. You can rerun these tests by using the class [`PerformanceTest`](gson/src/test/java/com/google/gson/metrics/PerformanceTest.java). @@ -62,25 +62,25 @@ Here are some metrics that we obtained on a desktop (dual opteron, 8GB RAM, 64-b Note: Delete the `disabled_` prefix to run these tests. We use this prefix to prevent running these tests every time we run JUnit tests. -## Gson Users +## Gson Users Gson was originally created for use inside Google where it is currently used in a number of projects. It is now used by a number of public projects and companies. -## Using Gson +## Using Gson The primary class to use is [`Gson`](gson/src/main/java/com/google/gson/Gson.java) which you can just create by calling `new Gson()`. There is also a class [`GsonBuilder`](gson/src/main/java/com/google/gson/GsonBuilder.java) available that can be used to create a Gson instance with various settings like version control and so on. The Gson instance does not maintain any state while invoking JSON operations. So, you are free to reuse the same object for multiple JSON serialization and deserialization operations. -## Using Gson with Gradle/Android +## Using Gson with Gradle/Android ```gradle dependencies { - implementation 'com.google.code.gson:gson:2.10' + implementation 'com.google.code.gson:gson:2.10.1' } ``` -## Using Gson with Maven +## Using Gson with Maven To use Gson with Maven2/3, you can use the Gson version available in Maven Central by adding the following dependency: @@ -90,7 +90,7 @@ To use Gson with Maven2/3, you can use the Gson version available in Maven Centr com.google.code.gson gson - 2.10 + 2.10.1 compile @@ -98,7 +98,7 @@ To use Gson with Maven2/3, you can use the Gson version available in Maven Centr That is it, now your Maven project is Gson enabled. -### Primitives Examples +### Primitives Examples ```java // Serialization @@ -110,15 +110,15 @@ int[] values = { 1 }; gson.toJson(values); // ==> [1] // Deserialization -int one = gson.fromJson("1", int.class); -Integer one = gson.fromJson("1", Integer.class); -Long one = gson.fromJson("1", Long.class); -Boolean false = gson.fromJson("false", Boolean.class); +int i = gson.fromJson("1", int.class); +Integer intObj = gson.fromJson("1", Integer.class); +Long longObj = gson.fromJson("1", Long.class); +Boolean boolObj = gson.fromJson("false", Boolean.class); String str = gson.fromJson("\"abc\"", String.class); -String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class); +String[] strArray = gson.fromJson("[\"abc\"]", String[].class); ``` -### Object Examples +### Object Examples ```java class BagOfPrimitives { @@ -146,7 +146,7 @@ BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class); // ==> obj2 is just like obj ``` -#### **Finer Points with Objects** +#### **Finer Points with Objects** * It is perfectly fine (and recommended) to use private fields. * There is no need to use any annotations to indicate a field is to be included for serialization and deserialization. All fields in the current class (and from all super classes) are included by default. @@ -158,7 +158,7 @@ BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class); * Fields corresponding to the outer classes in inner classes are ignored and not included in serialization or deserialization. * Anonymous and local classes are excluded. They will be serialized as JSON `null` and when deserialized their JSON value is ignored and `null` is returned. Convert the classes to `static` nested classes to enable serialization and deserialization for them. -### Nested Classes (including Inner Classes) +### Nested Classes (including Inner Classes) Gson can serialize static nested classes quite easily. @@ -197,7 +197,7 @@ public class InstanceCreatorForB implements InstanceCreator { The above is possible, but not recommended. -### Array Examples +### Array Examples ```java Gson gson = new Gson(); @@ -215,7 +215,7 @@ int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); We also support multi-dimensional arrays, with arbitrarily complex element types. -### Collections Examples +### Collections Examples ```java Gson gson = new Gson(); @@ -235,12 +235,12 @@ Collection ints2 = gson.fromJson(json, collectionType); Fairly hideous: note how we define the type of collection. Unfortunately, there is no way to get around this in Java. -#### Collections Limitations +#### Collections Limitations Gson can serialize collection of arbitrary objects but can not deserialize from it, because there is no way for the user to indicate the type of the resulting object. Instead, while deserializing, the Collection must be of a specific, generic type. This makes sense, and is rarely a problem when following good Java coding practices. -### Maps Examples +### Maps Examples Gson by default serializes any `java.util.Map` implementation as a JSON object. Because JSON objects only support strings as member names, Gson converts the Map keys to strings by calling `toString()` on them, and using `"null"` for `null` keys: @@ -309,7 +309,7 @@ String json = gson.toJson(stringMap); // json is {"key":"value"} Note that when deserializing enums as Map keys, if Gson is unable to find an enum constant with a matching `name()` value respectively `@SerializedName` annotation, it falls back to looking up the enum constant by its `toString()` value. This is to work around the issue described above, but only applies to enum constants. -### Serializing and Deserializing Generic Types +### Serializing and Deserializing Generic Types When you call `toJson(obj)`, Gson calls `obj.getClass()` to get information on the fields to serialize. Similarly, you can typically pass `MyClass.class` object in the `fromJson(json, MyClass.class)` method. This works fine if the object is a non-generic type. However, if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. Here is an example illustrating the point: @@ -337,7 +337,7 @@ gson.fromJson(json, fooType); The idiom used to get `fooType` actually defines an anonymous local inner class containing a method `getType()` that returns the fully parameterized type. -### Serializing and Deserializing Collection with Objects of Arbitrary Types +### Serializing and Deserializing Collection with Objects of Arbitrary Types Sometimes you are dealing with JSON array that contains mixed types. For example: `['hello',5,{name:'GREETINGS',source:'guest'}]` @@ -376,7 +376,7 @@ However, deserialization with `fromJson(json, Collection.class)` will not work s This approach is practical only if the array appears as a top-level element or if you can change the field type holding the collection to be of type `Collection`. -### Built-in Serializers and Deserializers +### Built-in Serializers and Deserializers Gson has built-in serializers and deserializers for commonly used classes whose default representation may be inappropriate, for instance @@ -387,7 +387,7 @@ For many more, see the internal class [`TypeAdapters`](gson/src/main/java/com/go You can also find source code for some commonly used classes such as JodaTime at [this page](https://sites.google.com/site/gson/gson-type-adapters-for-common-classes-1). -### Custom Serialization and Deserialization +### Custom Serialization and Deserialization Sometimes default representation is not what you want. This is often the case when dealing with library classes (DateTime, etc). Gson allows you to register your own custom serializers and deserializers. This is done by defining two parts: @@ -407,7 +407,7 @@ gson.registerTypeAdapter(MyType.class, new MyInstanceCreator()); `registerTypeAdapter` call checks if the type adapter implements more than one of these interfaces and register it for all of them. -#### Writing a Serializer +#### Writing a Serializer Here is an example of how to write a custom serializer for JodaTime `DateTime` class. @@ -421,7 +421,7 @@ private class DateTimeSerializer implements JsonSerializer { Gson calls `serialize()` when it runs into a `DateTime` object during serialization. -#### Writing a Deserializer +#### Writing a Deserializer Here is an example of how to write a custom deserializer for JodaTime DateTime class. @@ -449,7 +449,7 @@ Often you want to register a single handler for all generic types corresponding Gson supports registering a single handler for this. You can also register a specific handler for a specific generic type (say `Id` needed special handling). The `Type` parameter for the `toJson()` and `fromJson()` contains the generic type information to help you write a single handler for all generic types corresponding to the same raw type. -### Writing an Instance Creator +### Writing an Instance Creator While deserializing an Object, Gson needs to create a default instance of the class. Well-behaved classes that are meant for serialization and deserialization should have a no-argument constructor. @@ -473,7 +473,7 @@ Type could be of a corresponding generic type * Very useful to invoke constructors which need specific generic type information * For example, if the `Id` class stores the class for which the Id is being created -#### InstanceCreator for a Parameterized Type +#### InstanceCreator for a Parameterized Type Sometimes the type that you are trying to instantiate is a parameterized type. Generally, this is not a problem since the actual instance is of raw type. Here is an example: @@ -482,7 +482,7 @@ class MyList extends ArrayList { } class MyListInstanceCreator implements InstanceCreator> { - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") public MyList createInstance(Type type) { // No need to use a parameterized list since the actual instance will have the raw type anyway. return new MyList(); @@ -513,9 +513,9 @@ class IdInstanceCreator implements InstanceCreator> { In the above example, an instance of the Id class can not be created without actually passing in the actual type for the parameterized type. We solve this problem by using the passed method parameter, `type`. The `type` object in this case is the Java parameterized type representation of `Id` where the actual instance should be bound to `Id`. Since `Id` class has just one parameterized type parameter, `T`, we use the zeroth element of the type array returned by `getActualTypeArgument()` which will hold `Foo.class` in this case. -### Compact Vs. Pretty Printing for JSON Output Format +### Compact Vs. Pretty Printing for JSON Output Format -The default JSON output that is provided by Gson is a compact JSON format. This means that there will not be any whitespace in the output JSON structure. Therefore, there will be no whitespace between field names and its value, object fields, and objects within arrays in the JSON output. As well, "null" fields will be ignored in the output (NOTE: null values will still be included in collections/arrays of objects). See the [Null Object Support](#TOC-Null-Object-Support) section for information on configure Gson to output all null values. +The default JSON output that is provided by Gson is a compact JSON format. This means that there will not be any whitespace in the output JSON structure. Therefore, there will be no whitespace between field names and its value, object fields, and objects within arrays in the JSON output. As well, "null" fields will be ignored in the output (NOTE: null values will still be included in collections/arrays of objects). See the [Null Object Support](#null-object-support) section for information on configure Gson to output all null values. If you would like to use the Pretty Print feature, you must configure your `Gson` instance using the `GsonBuilder`. The `JsonFormatter` is not exposed through our public API, so the client is unable to configure the default print settings/margins for the JSON output. For now, we only provide a default `JsonPrintFormatter` that has default line length of 80 character, 2 character indentation, and 4 character right margin. @@ -526,7 +526,7 @@ Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(someObject); ``` -### Null Object Support +### Null Object Support The default behaviour that is implemented in Gson is that `null` object fields are ignored. This allows for a more compact output format; however, the client must define a default value for these fields as the JSON format is converted back into its Java form. @@ -566,12 +566,12 @@ System.out.println(json); The output is: -``` +```json {"s":null,"i":5} null ``` -### Versioning Support +### Versioning Support Multiple versions of the same object can be maintained by using [@Since](gson/src/main/java/com/google/gson/annotations/Since.java) annotation. This annotation can be used on Classes, Fields and, in a future release, Methods. In order to leverage this feature, you must configure your `Gson` instance to ignore any field/object that is greater than some version number. If no version is set on the `Gson` instance then it will serialize and deserialize all fields and classes regardless of the version. @@ -601,17 +601,17 @@ System.out.println(jsonOutput); The output is: -``` +```json {"newField":"new","field":"old"} {"newerField":"newer","newField":"new","field":"old"} ``` -### Excluding Fields From Serialization and Deserialization +### Excluding Fields From Serialization and Deserialization -Gson supports numerous mechanisms for excluding top-level classes, fields and field types. Below are pluggable mechanisms that allow field and class exclusion. If none of the below mechanisms satisfy your needs then you can always use [custom serializers and deserializers](#TOC-Custom-Serialization-and-Deserialization). +Gson supports numerous mechanisms for excluding top-level classes, fields and field types. Below are pluggable mechanisms that allow field and class exclusion. If none of the below mechanisms satisfy your needs then you can always use [custom serializers and deserializers](#custom-serialization-and-deserialization). -#### Java Modifier Exclusion +#### Java Modifier Exclusion By default, if you mark a field as `transient`, it will be excluded. As well, if a field is marked as `static` then by default it will be excluded. If you want to include some transient fields then you can do the following: @@ -630,11 +630,11 @@ Gson gson = new GsonBuilder() .create(); ``` -#### Gson's `@Expose` +#### Gson's `@Expose` This feature provides a way where you can mark certain fields of your objects to be excluded for consideration for serialization and deserialization to JSON. To use this annotation, you must create Gson by using `new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()`. The Gson instance created will exclude all fields in a class that are not marked with `@Expose` annotation. -#### User Defined Exclusion Strategies +#### User Defined Exclusion Strategies If the above mechanisms for excluding fields and class type do not work for you then you can always write your own exclusion strategy and plug it into Gson. See the [`ExclusionStrategy`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/ExclusionStrategy.html) JavaDoc for more information. @@ -693,7 +693,7 @@ The output is: {"longField":1234} ``` -### JSON Field Naming Support +### JSON Field Naming Support Gson supports some pre-defined field naming policies to convert the standard Java field names (i.e., camel cased names starting with lower case --- `sampleFieldNameInJava`) to a JSON field name (i.e., `sample_field_name_in_java` or `SampleFieldNameInJava`). See the [FieldNamingPolicy](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/FieldNamingPolicy.html) class for information on the pre-defined naming policies. @@ -726,7 +726,7 @@ The output is: If you have a need for custom naming policy ([see this discussion](https://groups.google.com/group/google-gson/browse_thread/thread/cb441a2d717f6892)), you can use the [@SerializedName](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/SerializedName.html) annotation. -### Sharing State Across Custom Serializers and Deserializers +### Sharing State Across Custom Serializers and Deserializers Sometimes you need to share state across custom serializers/deserializers ([see this discussion](https://groups.google.com/group/google-gson/browse_thread/thread/2850010691ea09fb)). You can use the following three strategies to accomplish this: @@ -736,14 +736,14 @@ Sometimes you need to share state across custom serializers/deserializers ([see 1 and 2 are not thread-safe options, but 3 is. -### Streaming +### Streaming In addition Gson's object model and data binding, you can use Gson to read from and write to a [stream](https://sites.google.com/site/gson/streaming). You can also combine streaming and object model access to get the best of both approaches. -## Issues in Designing Gson +## Issues in Designing Gson See the [Gson design document](GsonDesignDocument.md "Gson design document") for a discussion of issues we faced while designing Gson. It also include a comparison of Gson with other Java libraries that can be used for JSON conversion. -## Future Enhancements to Gson +## Future Enhancements to Gson For the latest list of proposed enhancements or if you'd like to suggest new ones, see the [Issues section](https://github.com/google/gson/issues) under the project website. diff --git a/gson/pom.xml b/gson/pom.xml index 745d72b6..944e55dd 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -4,7 +4,7 @@ io.gitlab.jfronny gson-parent - 2.11.0-SNAPSHOT + 2.10.2-SNAPSHOT gson @@ -23,6 +23,12 @@ junit test + + com.google.truth + truth + 1.1.3 + test + @@ -81,7 +87,7 @@ biz.aQute.bnd bnd-maven-plugin - 6.3.1 + 6.4.0 @@ -97,7 +103,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.0.0-M8