Gson is a Java library used to implement the mutual conversion between Json and Java objects. Gson is an open source project hosted at https://github.com/google/gson.
The main class in Gson is Gson. You can also use the class GsonBuilder to set some options while creating Gson objects.
Gson objects do not save any state when processing Json, so users can easily perform multiple serialization, deserialization and other operations on the same Gson object.
Example: Basic use
//SerializationGson gson = new Gson();gson.toJson(1); //==> prints 1gson.toJson("abcd"); //==> prints "abcd"gson.toJson(new Long(10)); //==> prints 10int[] values = { 1 };gson.toJson(values); //==> prints [1]//Deserializationint one = gson.fromJson("1", int.class);Integer one = gson.fromJson("1", Integer.class);Long one = gson.fromJson("1", Integer.class);Long one = gson.fromJson("1", Long.class);Boolean f = gson.fromJson("false", Boolean.class);String str = gson.fromJson("/"abc/"", String.class);String anotherStr = gson.fromJson("[/"abc/"]", String.class);//SerializationBagOfPrimitives obj = new BagOfPrimitives();Gson gson = new Gson();String json = gson.toJson(obj); //==> json is {"value1":1,"value2":"abc"}Example: Conversion between an object and a Json
Define the BagOfPrimitives class:
class BagOfPrimitives { private int value1 = 1; private String value2 = "abc"; private transient int value3 = 3; BagOfPrimitives() { // no-args constructor }}Serialized to Json:
//SerializationBagOfPrimitives obj = new BagOfPrimitives();Gson gson = new Gson();String json = gson.toJson(obj); //==> json is {"value1":1,"value2":"abc"}Do not serialize objects containing circular references, otherwise infinite recursion will occur.
Deserialization:
//DeserializationBagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class); //==> obj2 is just like obj
Some details when processing objects:
Handling of nested classes (including inner classes)
Gson can easily serialize nested classes and deserialize static nested classes. Gson cannot automatically deserialize pure inner classes because the parameterless constructor of the inner class needs to refer to the object containing it (i.e., instances of the outer class). To deserialize a static class, you can statically either the inner class or provide a custom instance creator. Here is an example:
public class A { public String a; class B { public String b; public B() { // No args constructor for B } }}The above class B cannot be serialized by Gson. Since class B is a (non-static) inner class, Gson also cannot deserialize {"b":"abc"} into an instance of class B. If B is declared as static class B, then Gson can deserialize the string.
Another solution is to write an instance creator for B:
public class InstanceCreatorForB implements InstanceCreator<AB> { private final A a; public InstanceCreatorForB(A a) { this.a = a; } public AB createInstance(Type type) { return a.new B(); }}This method is feasible, but is not recommended. (The translator said he didn't understand this instance creator and didn't know how to use it)
Example: Array
Gson gson = new Gson();int[] ints = {1, 2, 3, 4, 5};String[] strings = {"abc", "def", "ghi"};//Serializationgson.toJson(ints); ==> prints [1,2,3,4,5]gson.toJson(strings); ==> prints ["abc", "def", "ghi"]//Deserializationint[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);==> ints2 will be same as intsGson also supports multidimensional arrays with complex data types.
Example: Collection
Gson gson = new Gson();Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);//SerializationString json = gson.toJson(ints); //==> json is [1,2,3,4,5]//DeserializationType collectionType = new TypeToken<Collection<Integer>>(){}.getType();Collection<Integer> ints2 = gson.fromJson(json, collectionType);//ints2 is same as intsLimitations when processing collections:
Serialize/deserialize generics
When using toJson(obj), Gson calls obj.getClass() to get field information for use in serialization. Similarly, the object MyClass.class can be passed as a parameter to the fromJson(json, MyClass.class) method, which can be used when the object is not a generic. However, when the object is a generic type object, the generic type information will be lost due to the Type Erasure mechanism in Java. The following example illustrates this:
class Foo<T> { T value;}Gson gson = new Gson();Foo<Bar> foo = new Foo<Bar>();gson.toJson(foo); // May not serialize foo.value correctlygson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as BarThe above code interprets the value as the Bar type, because Gson calls foo.getClass() to get the information of the class, but this method returns a primitive class, namely Foo.class. This means that Gson cannot know that this is an object of type Foo<Bar>.
To solve this problem, you can specify the correct parameterized type for your generics. You can use the TypeToken class to do:
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();gson.toJson(foo, fooType);gson.fromJson(json, fooType);fooType actually defines an anonymous inner class, which contains a getType() method that can return all parameterized types.
Serialize/deserialize a collection of objects of any type
Sometimes the processed JSON contains mixed types, such as:
['hello',5,{name:'GREETINGS',source:'guest'}]The corresponding set should be:
Collection collection = new ArrayList();collection.add("hello");collection.add(5);collection.add(new Event("GREETINGS", "guest"));The Event class is defined as follows:
class Event { private String name; private String source; private Event(String name, String source) { this.name = name; this.source = source; }}With Gson, you don't need to do anything special to serialize the collection: toJson(collection) will output satisfactory results.
However, deserialization through fromJson(json, Collection.class) is not possible, because Gson cannot correspond to the content in json with the type. Gson requires you to provide a common version of the collection type in fromJson. You have three choices:
Solution 1: Use the Gson parser's API (low-level stream parser or DOM parser JsonParser) to parse array elements, and then use Gson.fromJson() to process each array element. This is the preferred solution.
Scheme 2: Register a type adapter for Collection.class to map elements in the array to the appropriate object. The disadvantage of this method is that it will cause inconvenience when dealing with other collection types.
Scheme 3: Register a type adapter for MyCollectionMemberType, and use Collection<MyCollectionMemberType> in fromJson. This method is only possible if the array looks like a high-level element or if you can change the field type to Collection<MyCollectionMemberType>.
Built-in serializer/deserializer
Gson provides a serializer/deserializer for commonly used classes that may not be appropriate for default representation.
Here is a list of these classes:
Custom serialization/deserialization
Sometimes, the default implementation of Gson is not what you want, which is more common when dealing with some class libraries (such as DateTime).
Gson allows you to register custom serializers/deserializers. To do this, you need to implement the following parts:
Json serializer: Need to customize serialization for an object
Json deserializer: You need to customize the deserialization class creator for a type: If there is a parameterless constructor or a deserializer has been registered, it is not necessary.
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());
registerTypeAdapter checks whether the type adapter implements multiple interfaces and registers the type adapter for these interfaces.
Write a serializer
Here is an example of customizing the serializer for DateTime:
private class DateTimeSerializer implements JsonSerializer<DateTime> { public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); }}Gson calls toJson() when serializing DateTime instances.
Write a deserializer
The following example shows how to write a deserializer of the DateTime class:
private class DateTimeDeserializer implements JsonDeserializer<DateTime> { public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return new DateTime(json.getAsJsonPrimitive().getAsString()); }}When Gson needs to deserialize a JSON string to a DateTime object, fromJson() will be called.
For serializers/deserializers, attention should be paid to:
Write an instance creator
When deserializing an object, Gson needs to create an instance of a class. A class that performs well during serialization/deserialization means that this class has a parameterless constructor. Typically, when dealing with classes without parameterless constructors in a class library, an instance creator is required.
Example creator example:
private class MoneyInstanceCreator implements InstanceCreator<Money> { public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); }}Parameterized type instance creator
Sometimes the type to be instantiated will be a parameterized type. Overall, since the real instance is a primitive type, this is not a problem. Here is an example:
class MyList<T> extends ArrayList<T> {}class MyListInstanceCreator implements InstanceCreator<MyList<?>> { @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(); }}However, sometimes you need to create instances based on real parameterized types. In this case, you can pass the type parameter to the createInstance method. Here is 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(Type type) { Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); Type idType = typeParameters[0]; // Id has only one parameterized type T return Id.get((Class)idType, 0L); }}In the example above, an instance of the Id class cannot be created without passing the true type to the parameterized type. We can solve this problem by passing the parameter type to the method. Here, the type object can be regarded as a representation of the Java parameterized type of Id<Foo>, and the corresponding instance should be bound to Id<Foo>. Since class Id only has one parameter T of parameter, we use getActualTypeArgument() to return the 0th element of the type array, in this example, Foo.class.
Compact output vs beautiful output
The default output of Json in Gson is in compact JSON format. That is to say, there are no extra whitespace characters in JSON. Therefore, there is no blank space between the field name and field value, between fields, and between array elements in the output of JSON. Also, the null field will not be output (note: null will be preserved in collection and array objects).
If you want to output more beautifully, you need to use GsonBuilder to configure Gson instances. JsonFormatter does not exist in the public API, so the client cannot configure the default output settings. Now we only provide JsonPrintFormatter, which by default is 80 characters per line, indentation uses 2 characters and right margin is 4 characters.
The following example shows how to get a Gson instance to use a JsonPrintFormatter instead of using the default JsonCompactFormatter.
Gson gson = new GsonBuilder().setPrettyPrinting().create();String jsonOutput = gson.toJson(someObject);
Empty object
In Gson's default implementation, null objects are ignored. This can make the output format (which can be considered as the result of serialization) tighter; however, the client must define a default value for it so that JSON can deserialize normally.
If you want to make a Gson instance serializable null, you can:
Gson gson = new GsonBuilder().serializeNulls().create();
Note that when serializing null, a JsonNull element will be added to the JsonElement structure. Therefore, we can use this object (gson) in our custom serializer/deserializer.
Here is 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);Output:
{"s":null,"i":5}nullVersion support
You can use the @Since annotation to maintain multiple versions of the same object. This annotation can be used on classes and fields, and will also be supported on methods in the future. To use this feature, you need to configure the Gson instance to ignore fields and objects larger than a certain version number. If the version is not set in the Gson object, all fields and classes are used when serializing/deserializing.
public class VersionedClass { @Since(1.1) private final String newerField; @Since(1.0) private final String newField; private final String field; public VersionedClass() { 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);Output:
{"newField":"new","field":"old"}{"newerField":"newer","newField":"new","field":"old"}Exclude fields from serialization/deserialization
Gson supports the use of many methods to remove classes, fields, and field types. If the following method does not meet your needs, you can use the custom serialization/deserializer method.
1.Java Modifier Exclusion
By default, if a field is declared as transient, the field is excluded. In addition, if a field is declared static, this field will also be excluded by default. If you want to include some fields declared as transient, you can do this:
import java.lang.reflect.Modifier;Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC) .create();
Note that in the excludeFieldsWithModifiers method, you can use as many Modifier constants as possible. For example:
Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) .create();
2. Use the @Expose field to exclude
This feature allows you to tag specific fields in the class so that they are not excluded/excluded in serialization/deserialization. To use this annotation, you should create a Gson using new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(). The Gson instance will exclude all fields in the class that are not marked by @Expose.
3. User-defined exclusion policy
If the above exclusion method cannot meet the needs, you can also customize your exclusion strategy. For more information, please refer to ExclusionStrategy JavaDoc.
The following example shows how to exclude fields marked with @Foo, and exclude top-level types of String class or declared field types:
@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; public SampleObjectForTest() { 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) { return f.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); }Output:
{"longField":1234}Support for JSON field naming
Some predefined field naming strategies of Gson can convert standard Java field names (that is, camel nomenclature, such as sampleFieldNameInJava) into a Json field name (that is sample_field_name_in_java or SampleFieldNameInJava). For more information, please refer to FieldNamingPolicy.
Gson also has an annotation-based policy to enable clients to customize the names of fields. Under this strategy, if an illegal field name is provided as the annotated value, Gson will throw a Runtime exception.
The following example shows how to use these two Gson naming strategies:
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);Output:
{"custom_naming":"first","SomeOtherField":"second"}If you want to customize the name, you can use the @SerializedName annotation.
Sharing state between serializer and deserializer
Sometimes you will need to share state between the serializer and the deserializer, and you can use the following three methods to achieve your goal:
The first two methods are not thread-safe, the third is.
Solution to GSON parsing null error
One disadvantage of GSON is that it cannot set null replacement.
We can only manually replace the null returned by the server in batches. When the normal interface is defined, the server will definitely not be allowed to return null, but the background result will always appear null!
If you search, there is a common answer,
Gson gson = new GsonBuilder().serializeNulls().create();
But this cannot solve the desequence problem, how to solve it?
The solution is as follows:
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()).create();//Then use the gson written in the above line to serialize and deserialize the entity class typegson.fromJson(json, type);gson.toJson(type);//NullStringToEmptyAdapterFactory code public class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory { @SuppressWarnings("unchecked") public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { Class<T> rawType = (Class<T>) type.getRawType(); if (rawType != String.class) { return null; } return (TypeAdapter<T>) new StringNullAdapter(); }}// StringNullAdapter code public class StringNullAdapter extends TypeAdapter<String> { @Override public String read(JsonReader reader) throws IOException { // TODO Auto-generated method stub if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return ""; } return reader.nextString(); } @Override public void write(JsonWriter writer, String value) throws IOException { // TODO Auto-generated method stub if (value == null) { writer.nullValue(); return; } writer.value(value); }}