Original text: github.com/google/gson…
General situation of
Gson is a Java library that can be used to convert Java objects into JSON expressions, and in turn JSON strings into Java objects. Gson can be used with any Java object, including existing objects for which you don’t have the source code
The goal of Gson
- Provide an easy-to-use mechanism similar to
toString()
And constructors (factory patterns) are used to convert Java and JSON to and from each other - Allows you to convert pre-existing but unmodifiable objects to or from JSON
- Allows a custom representation of an object
- Support for any complex object
- Generate compact, readable JSON output
Performance and scalability of Gson
Some of the parameters provided here are taken from a number of test cases that have been run on our notebook (dual-core AMD Holon processor, 8GB RAM, 64-bit Ubuntu system), and you can also re-run these test cases using the PerformanceTest class
-
For string: deserialization of more than 25 MB string without any problems (can view PerformanceTest method disabled_testStringDeserializationPerformance)
-
For large collections:
- Serialized through a collection of 14 million objects (check it out
PerformanceTest
Under thedisabled_testLargeCollectionSerialization
Method) – deserialize a collection of 87,000 objects (seePerformanceTest
Under thedisabled_testLargeCollectionDeserialization
Methods)
- Serialized through a collection of 14 million objects (check it out
-
Version 1.4 of Gson increased the maximum deserialization of byte arrays and collections from 80KB to 11MB
Tip: Remove the disabled_ prefix when running these test cases, which we use to avoid running the above example every time we run the JUnit test
Gson user
Gson was originally created for use by Google insiders and is used in many current Google projects. It is now used by many public projects and companies
Using Gson
The main Gson class is Gson, which you can create directly by calling new Gson(), or you can use the GsonBuilder class to create instances of Gson and set parameters like version control
The Gson instance does not retain any state when you call Json operations, so you can arbitrarily reuse an object for multiple Json serialization and deserialization operations
Android: Use Gson on Gradle
dependencies {
implementation 'com. Google. Code. Gson: gson: 2.8.5'
}
Copy the code
Use Gson on Maven
To use Gson in Maven2/3, you can find the appropriate version of Gson in the Maven library to add to the dependency below
<dependencies> <! -- Gson: Java to Json conversion --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> The < version > 2.8.5 < / version > < scope > compile < / scope > < / dependency > < / dependencies >Copy the code
Then your Maven project can use Gson
The basic example
// serialize Gson Gson = new Gson(); gson.toJson(1); // ==> 1 gson.toJson("abcd"); / / = = >"abcd"gson.toJson(new Long(10)); // ==> 10 int[] values = { 1 }; gson.toJson(values); // ==> [1] // serialize 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);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
Copy the code
Examples of objects
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {primitives obj = new primitives (); Gson gson = new Gson(); String json = gson.toJson(obj); // ==> json is {"value1": 1,"value2":"abc"}
Copy the code
Note that you cannot serialize an object with a loop call, otherwise it will return an infinite recursive result
// Serialize BagOfPrimitives obj2 = gson.fromjson (json, primitives. // ==> obj2 is just like objCopy the code
Object details
- Using private in objects is perfectly acceptable, and we recommend you do it
- There is no need to declare which regions need to be serialized or deserialized, and all regions of the current class (including all superclasses) are included by default
- If a field is marked transient, it is (by default) ignored and not included in JSON serialization or deserialization
- This implements this handling of Null values
- When serialized, Null fields are omitted from the output.
- When deserializing, the default is set when the region in the class is not found in the JSON result: the object is set to null, the numeric type is set to 0, and the Boolean type is set to false
- If the type is syntactic, it is ignored and is not included in JSON serialization or deserialization
- Fields corresponding to inner classes, anonymous classes, and outer classes in local classes are ignored and not included in serialization or deserialization.
Nested classes (containing inner classes)
Gson makes it easy to serialize statically nested classes
Gson can also deserialize statically nested classes. However, Gson cannot automatically deserialize pure inner classes because their no-argument constructors also need to reference contained objects that have not yet been deserialized. You can solve this problem by making an inner class a static inner class or providing it with a custom InstanceCreator. Here’s an example:
public class A {
public String a;
class B {
public String b;
public B() {// B's no-argument constructor}}}Copy the code
Note: The above class B cannot (by default) use Gson serialization.
{“b”:” ABC “} Since class B is an inner class, Gson cannot deserialize to an instance of B. If it is defined as static class B, then Gson can deserialize the string. Another solution is to write a custom instance creator for B.
public class InstanceCreatorForB implements InstanceCreator<A.B> {
private final A a;
public InstanceCreatorForB(A a) {
this.a = a;
}
public A.B createInstance(Type type) {
returna.new B(); }}Copy the code
The above code can run but is not recommended
An example of arrays
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc"."def"."ghi"}; // Serialization gson.toJson(ints); / / = = > [1, 2, 3, 4, 5]. Gson toJson (strings); / / = = > ["abc"."def"."ghi"]
// Deserialization
int[] ints2 = gson.fromJson("[1, 2, 3, 4, 5]", int[].class);
// ==> ints2 will be same as ints
Copy the code
We also support multi-size arrays and any complex element type
Set example
Gson gson = new Gson(); Collection < Integer > ints = Lists. ImmutableList (1, 2, 3, 4, 5); // Serialization String json = gson.toJson(ints); // ==> json is [1,2,3,4,5] // Deserialization Type collectionType = new TypeToken<Collection<Integer>>(){}.gettype (); Collection<Integer> ints2 = gson.fromJson(json, collectionType); // ==> ints2 is same as intCopy the code
Scary phenomenon: Notice how we defined the collection type? Unfortunately, you can’t do that in Java
Collection limitations: Gson can serialize a collection of any object, but cannot deserialize from it, because the user cannot know the type of the resulting object. Instead, when deserializing, the collection must be a specific generic type. As you all know, there are fewer problems when good coding practices are followed
When serializing and deserializing generics
When you call toJson(obj), Gson will call obj.getClass() to get the information for the region to serialize. Again, you can use the fromJson(json, myClass.class) method on the myClass.class object. This is fine if there are no generics in the object. However, if you have generics in your object, the generics will be lost because Java types are erased. Here’s an example of this
class Foo<T> {
T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // This might not serialize foo.value properly
gson.fromJson(json, foo.getClass()); // Foo. Value cannot be deserialized as Bar
Copy the code
The above code attempts to assign the type Bar to value but fails because Gson calls list.getClass() to get information about the class, but returns a pure class foo.class. This means that Gson has no way of knowing that your object is of type Foo
instead of Foo.
You can solve this problem by specifying the correct parameters instead of generics. You can also use TypeToken classes
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
Copy the code
The conventional approach is to get fooType to actually define an anonymous inner class containing the getType() method that returns the entire parameter type
Serialize and deserialize collections with objects of any type
Sometimes you need to deal with JSON arrays with cluttered types, such as [‘hello’,5,{name:’GREETINGS’,source:’guest’}]. Equivalent collections contain:
Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS"."guest"));
Copy the code
The Event class is defined as:
class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source; }}Copy the code
You can serialize the collection directly with Gson without doing anything else, and toJson(Collection) will write the output you expect
However, deserializing fromJson(JSON, collection.class) doesn’t work because Gson doesn’t know how to map the input to the type. Gson asks you to provide a generic version of the collection type, fromJson(). So, you have three options:
-
Array elements are parsed using Gson’s parser API (low-level stream parser or DOM parser JsonParser), and then gson.fromjson () is used on each array element. This is the preferred method. Is this an example of how to do this
-
Register a type adapter, Collection.class, to look at each array member and map them to the appropriate objects. The downside of this approach is that it can mess up the deserialization of other collection types in Gson.
-
Register an adapter of type MyCollectionMemberType and use fromJson() with Collection
.
This method is only available if the array is displayed as a top-level element or if you can change the field type that keeps the Collection as a type.
Built-in serializer and deserializer
Gson has built-in serializers and deserializers for common classes, whose default representation may not be appropriate. Here’s a list of these:
java.net.URL
Match it to a string"https://github.com/google/gson/"
java.net.URI
Match it to a string"/google/gson/"
You can also find the source code for some common classes, such as JodaTime, on this page.
Custom serialization and deserialization
Sometimes the default 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:
-
Json Serializers: Need to define custom serialization for objects
-
Json Deserializers: Need to define custom Deserializers for types
-
Instance Creators: This is not required if no parameter constructor is available or if the deserializer is registered
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
Copy the code
The registerTypeAdapter call checks if the type adapter implements more than one of these interfaces and registers it for all of them.
To write the Json Serializers
Here is an example of how to write a custom serializer for the JodaTime DateTime class.
private class DateTimeSerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
returnnew JsonPrimitive(src.toString()); }}Copy the code
Gson calls serialize() when it encounters a DateTime object during serialization.
To write the Json Deserializers
Here is an example of how to write a custom deserializer for the JodaTime DateTime class.
private class DateTimeDeserializer implements JsonDeserializer<DateTime> { public DateTime deserialize(JsonElement json, TypetypeOfT, JsonDeserializationContext context)
throws JsonParseException {
returnnew DateTime(json.getAsJsonPrimitive().getAsString()); }}Copy the code
Gson calls deserialize when it needs to deserialize a JSON string fragment into a DateTime object
Details of serializers and deserializers
Typically, you want to register a single handler for all generic types that correspond to primitive types
- For example, suppose you have one
Id
Classes for ID representation/transformation (that is, internal versus external representation). Id<T>
Have the same serialized type for all generic types- Basically write out the id value
- Deserialization is very similar but not identical
- You need to call
new Id(Class<T>, String)
Which returns an instanceId<T>
- You need to call
Gson supports registering a handler for this. You can also register specific handlers for specific generic types (such as Id
RequiresSpecialHandling). The toJson() and fromJson() arguments in Type contain information about generic types to help you write a single handler for all generic types that correspond to the same primitive Type.
Write the Instance Creators
When deserializing an Object, Gson needs to create a default instance of the class. A good class for serialization and deserialization should have a no-argument constructor.
- With the use
public
orprivate
Has nothing to do
In general, instance creators are required when dealing with library classes that do not define parameterless constructors
The Instance Creators sample
private class MoneyInstanceCreator implements InstanceCreator<Money> {
public Money createInstance(Type type) {
return new Money("1000000", CurrencyCode.USD); }}Copy the code
A type can be a corresponding generic type
- Useful for calling constructors that require information about a particular generic type
- For example, if
Id
Class stores the class for which the Id is being created
Parameterized type of InstanceCreator
Sometimes the type you try to instantiate is a parameterized type. Usually, this is not a problem because the actual instance is primitive. Here’s an example:
class MyList<T> extends ArrayList<T> { } class MyListInstanceCreator implements InstanceCreator<MyList<? >> { @SuppressWarnings("unchecked") public MyList<? > createInstance(Typetype) {
// No need to use a parameterized list since the actual instance will have the raw type anyway.
returnnew MyList(); }}Copy the code
However, sometimes you need to create instances based on actual parameterized types. In this case, you can use the type parameter passed to the createInstance method. Here’s an example:
public class Id<T> { private final Class<T> classOfId; private final long value; public Id(Class<T> classOfId, long value) { this.classOfId = classOfId; this.value = value; } } class IdInstanceCreator implements InstanceCreator<Id<? >> { public Id<? > createInstance(Typetype) {
Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments();
Type idType = typeParameters[0]; // Id has only one parameterized type T
returnId.get((Class)idType, 0L); }}Copy the code
In the example above, you cannot create an instance of the Id class without actually passing in the actual type of the parameterized type. We solve this problem by using passed method parameters type. Type In this case, the object is a Java parameterized type representing the location Id
to which the actual instance should be bound. Since Idclass has only one parameterized type argument, T we use the 0th element of the returned type array, in this case getActualTypeArgument() which will hold foo.class.
JSON output format: Compact output vs. elegant output
The default JSON output provided by Gson is in compact JSON format. This means that there will be no Spaces in the output JSON structure. Therefore, there are no Spaces between the field names and their values in the JSON output, and between the object fields and the objects in the array. Again, the “NULL” field will be ignored in the output (note: null values will still be included in the collection/array of objects). For information on configuring Gson to output all Null values, see Null Object Support.
If you want to use the elegant output feature, you must use GsonBuilder to create a Gson instance. JsonFormatter is not exposed in our public API, so the user cannot configure or adjust the JSON output form. Currently, we only provide JsonPrintFormatter with a default line length of 80 characters, 2 character indentation, and 4 character right margin.
Here is an example of how to configure a Gson instance to use the default JsonPrintFormatter instead of JsonCompactFormatter:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);
Copy the code
Null object support the default behavior null implemented in Gson is to ignore object fields. This allows for a more compact output format; However, the user must define default values for these fields because the JSON format will be converted back to its Java form.
Here is how to configure Gson instance to output NULL:
Gson gson = new GsonBuilder().serializeNulls().create();
Copy the code
Note: When null is serialized using Gson, it adds a JsonNull element to the JsonElement structure. Therefore, this object can be used for custom serialization/deserialization.
Here’s an example:
public class Foo {
private final String s;
private final int i;
public Foo() {
this(null, 5);
}
public Foo(String s, int i) {
this.s = s;
this.i = i;
}
}
Gson gson = new GsonBuilder().serializeNulls().create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);
json = gson.toJson(null);
System.out.println(json);
Copy the code
The output is:
{"s":null,"i":5}
null
Copy the code
Version control support
Multiple versions of the same object can be maintained using the @since annotation. This annotation can be used for classes, fields, and methods in future releases. To take advantage of this feature, the Gson instance must be configured to ignore any fields/objects larger than a certain version number. If no version is set on the Gson instance, it will serialize and deserialize all fields and classes regardless of the version.
Public class VersionedClass {@since (1.1) private Final String newerField; @since (1.0) private Final String newField; private final String field; publicVersionedClass() {
this.newerField = "newer";
this.newField = "new";
this.field = "old";
}
}
VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
System.out.println();
gson = new Gson();
jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
Copy the code
The output is:
{"newField":"new"."field":"old"}
Copy the code
Exclude fields from serialization and deserialization
Gson supports a number of mechanisms for excluding top-level classes, fields, and field types. Listed below are pluggable mechanisms that allow field and class exclusion. If none of the following mechanisms satisfy your needs, you can always use custom serializers and deserializers.
Java modifiers excluded
By default, if a field is marked transient, it is excluded. Also, if a field is marked static it is excluded by default. If you want to include transient fields, do the following:
import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC)
.create();
Copy the code
Note: you can provide any number of methods excludeFieldsWithModifiers Modifier constant. Such as:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
.create();
Copy the code
The GSON@Expose
This feature provides a way for you to mark certain fields of objects to be excluded as serialized and deserialized to JSON. To use this notation, must use to create a Gson new GsonBuilder () excludeFieldsWithoutExposeAnnotation (). The create (). The Gson instance created will exclude any fields in the class that are not annotated with the @Expose annotation.
User-defined exclusion policy
If the above mechanism for excluding fields and class types doesn’t work for you, you can always write your own exclusion policy and plug it into Gson. See JavaDoc for more information about ExclusionStrategy.
The following example shows how to exclude fields marked with a specific @foo annotation and exclude the top-level type (or declared field type) of the class, String.
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface Foo { // Field tag only annotation } public class SampleObjectForTest { @Foo private final int annotatedField; private final String stringField; private final long longField; private final Class<? > clazzField; publicSampleObjectForTest() {
annotatedField = 5;
stringField = "someDefaultValue"; longField = 1234; } } public class MyExclusionStrategy implements ExclusionStrategy { private final Class<? >typeToSkip; private MyExclusionStrategy(Class<? >typeToSkip) {
this.typeToSkip = typeToSkip; } public boolean shouldSkipClass(Class<? > clazz) {return (clazz == typeToSkip);
}
public boolean shouldSkipField(FieldAttributes f) {
returnf.getAnnotation(Foo.class) ! = null; } } public static void main(String[] args) { Gson gson = new GsonBuilder() .setExclusionStrategies(new MyExclusionStrategy(String.class)) .serializeNulls() .create(); SampleObjectForTest src = new SampleObjectForTest(); String json = gson.toJson(src); System.out.println(json); }Copy the code
The output is:
{"longField": 1234}Copy the code
JSON field naming support
Gson supports some predefined field naming strategies, Convert the standard Java field name (sampleFieldNameInJava, the hump name beginning with a lowercase letter –) to the Json field name (sample_field_name_in_java or sampleFieldNameInJava). For information about predefined naming policies, see the FieldNamingPolicy class.
It also has annotation-based policies that allow users to define custom names based on each field. Note that annotation-based policies have field name validation, and a “runtime” exception will be thrown if an invalid field name is provided as a comment value.
Here is an example of how to use the two Gson naming policy features:
private class SomeObject {
@SerializedName("custom_naming") private final String someField;
private final String someOtherField;
public SomeObject(String a, String b) {
this.someField = a;
this.someOtherField = b;
}
}
SomeObject someObject = new SomeObject("first"."second");
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);
Copy the code
The output is:
{"custom_naming":"first"."SomeOtherField":"second"}
Copy the code
If you need a custom naming policy (see this discussion), you can use the annotation @serializedName.
Share state between custom serializers and deserializers
Sometimes you need to share state between custom serializers/deserializers (see this discussion). You can use the following three strategies to accomplish this:
- Store the state of the share in static fields
- Declare the serializer/deserializer as an inner class of the parent type and use the instance field of the parent type to store the shared state
- Using Java
ThreadLocal
1 and 2 are not thread-safe options, but 3 is.
flow
In addition to Gson’s object model and data binding, you can use Gson to read and write into streams. You can also combine flow and object model access to get the best of both approaches.
Problems encountered in designing Gson
Refer to the Gson design documentation for a discussion of the problems we encountered in designing Gson. It also includes a comparison of Gson with other Java libraries available for Json transformation.
Future improvements to Gson
For the latest updates or if you have new suggestions, please refer to Issues under the project website. Part of the