Generics:
- Juejin. Cn/post / 684490…
- www.jianshu.com/p/5179ede4c…
Gson Usage Guide
- An overview of
- The goal of Gson
- Gson performance and scalability
- Gson user
- Using Gson (in this case android)
- Use Gson in Gradle/Android
- example
- Object instance
- Object details
- Nested classes (including inner classes)
- An array of sample
- Set the sample
- Serialize and deserialize generic types
- Serialization and deserialization of collections of objects of any type
- Built-in serializer and deserializer
- Custom serialization and deserialization
- Write a serializer
- Write a deserializer
- Write the instance creator
- Compact VS JSON’s beautiful output format
- Empty object support
- Version control support
- Exclude fields from serialization and deserialization
- JSON field naming support
- State is shared between custom serialization and deserialization
- flow
- Design Gson problems
- Future improvements to Gson
An overview of
Gson is a Java library that can be used to transform Java objects into their JSON representation. It can also be used to convert JSON strings into equivalent Java objects. Gson can handle any Java object, including objects that are not preset in code.
The goal of Gson
- Provide easy-to-use mechanisms, such as the toString () constructor (factory method), to convert Java to JSON and vice versa
- Allows conversion of pre-existing immutable objects to or from JSON
- Allows a custom representation of an object
- Arbitrarily complex objects are supported
- Generate compact and readable JSON output
Gson performance and scalability
Gson user
The use of Gson
The main class to use is Gson, which can be created by calling New Gson() and the GsonBuilder class for creating instances of Gson with various Settings such as version control. The Gson instance does not maintain any state when the Json operation is invoked. Therefore, you can reuse the same Gson object to serialize and deserialize multiple Json objects freely.
Use Gson in Gradle/Android
Dependencies {implementation 'com. Google. Code. Gson: gson: 2.8.5'}Copy the code
Specific version, please refer to https://github.com/google/gson
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] / / deserialization int one. = gson fromJson (" 1 ", int. J 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
Object instance
class BagOfPrimitives{ private int value1 = 1; private String value2 = "abc"; private transient int value3 = 3; // primitives obj = new primitives (); Gson gson = new Gson(); String json = gson.toJson(obj); {"value1":1,"value2":" ABC "} // Deserialize BagOfPrimitives obj2 = gson. // ==> obj2 is an object like objCopy the code
Object details:
- It is best to decorate fields with private
- You do not need to use any comments to indicate which fields to serialize and deserialize. By default, all fields of the current class (including all fields inherited from its parent) are included
- If a field is transient, it will be ignored during serialization and deserialization
- The Gson implementation handles null values correctly
- When serializing, an empty field is ignored
- When deserializing, the missing entries in the JSON cause the corresponding fields in the object to be set to their default values when deserializing: object type null, numeric type zero, and Boolean false.
- If the field is synthesized, it is ignored and is not included in the JSON serialization or deserialization.
- Fields of inner, anonymous, and local classes are ignored and not included in serialization or deserialization
Nested classes (including inner classes)
Gson can serialize and deserialize statically nested classes. However, pure inner classes cannot be serialized and deserialized by Gson, since de-sequence requires a reference to an instance to get the actual type. You can solve this problem by using static inner classes or providing custom Instancecreators for them, such as:
public class A { public String a; class B { public String b; Public B() {// no arguments of B}}}Copy the code
Note: The above class B cannot be serialized by Gson. Gson can be serialized and deserialized by defining B as a static inner class, for example:
public class A { public String a; static class B { public String b; Public B() {// no arguments of B}}}Copy the code
It can also be solved by a custom creator, such as:
public class InstanceCreatorForB implements InstanceCreator<A.B> { private final A a; public InstanceCreatorForB(A a) { this.a = a; } public A.B createInstance(Type type) { return a.new B(); }}Copy the code
The above method is not recommended.
An array of sample
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 intsCopy the code
Set the sample
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 intsCopy the code
This is not recommended, but it does not solve this problem in JAVA. Collection limitations: Gson can serialize a collection of any object, but cannot deserialize from it, because the user cannot indicate the type of the resulting object. Instead, when deserializing, a Collection must be a specific generic type.
Serialize and deserialize generic types
When you call toJson(obj),Gson calls obj.getClass() to get information about the fields to be sequenced. Also, you can pass the myClass. class argument to fromJson(json, myClass.class). This method works if the object is of a non-generic type. However, if the object is a generic type, the generic type information is lost due to Java type erasure. Such as:
class Foo<T> { T value; } Gson gson = new Gson(); Foo<Bar> foo = new Foo<Bar>(); gson.toJson(foo); // Can't serialize gson.fromjson (json, foo.getClass()) properly; // Cannot be deserialized correctlyCopy the code
The above example code does not deserialize the Bar type properly because Gson calls list.getClass() to retrieve the class information, but returns foo.class. This means that Gson has no way of knowing that this is an object of type Foo and not just plain Foo. You can solve this problem by specifying the correct parameterized type for generic types. You can do this using the TypeToken class. Such as:
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
Copy the code
FooType actually defines an anonymous local inner class that contains a getType() method that returns a fully parameterized type.
Serialization and deserialization of collections of objects of any type
Sometimes, you’ll deal with JSON arrays that have multiple data types, such as:
['hello',5,{name:'GREETINGS',source:'guest'}]
Copy the code
This is equivalent to a Collection like this:
class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
}
Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));
Copy the code
You can call toJson(Collection) directly to get the above JSON array string. Deserializing fromJson(JSON, collection.class), however, failed because Gson did not know how to match data types. Gson requires you to provide a generic version of the collection type individual fromJson(). 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. Here is 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 disadvantage of this approach is that it affects the deserialization of other collection types in Gson.
- Register an adapter of type MyCollectionMemberType using fromJson() and Collection.
Collection 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 serialization and deserialization
Gson has built-in serializers and deserializers for common classes, such as JodaTime
Custom serialization and deserialization
Sometimes the default is not what you want. This is often the case when dealing with library classes such as DateTime. Gson allows you to register your own custom serializers and deserializers. This is done by defining two parts:
- Json Serializers: Mandatory, define custom serialization for objects
- Json Deserializers: Mandatory, custom Deserializers for types
- Instancecreators: Optional, not required if no parameter constructor or Json Deserializers are registered
Example:
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 call to registerTypeAdapter checks to see if the TypeAdapter implements one of the interfaces and registers all of them.
Realize the Serializer
private class DateTimeSerializer implements JsonSerializer<DateTime> { public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); }}Copy the code
Gson calls serialize() when it encounters a DateTime object during serialization.
Implement Deserializers
private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return new DateTime(json.getAsJsonPrimitive().getAsString());
}
}
Copy the code
Gson calls Deserialize when it encounters a DateTime object during deserialization.
Custom serialization and deserialization examples:
// Custom Serialize public class DogSerializer implements JsonSerializer<dog> { @Override public JsonElement serialize(Dog src, Type typeOfSrc, JsonSerializationContext context) { // This method gets involved whenever the parser encounters the Dog // object (for which this serializer is registered) JsonObject object = new JsonObject(); String name = src.getName().replaceAll(" ", "_"); object.addProperty("name", name); // we create the json object for the dog and send it back to the // Gson serializer return object; } public static void main(String[] args) { Animal<Dog> animal = new Animll<Dog>(); Dog dog = new Dog("I am a dog"); animal.setAnimal(dog); // Create the GsonBuilder and register a serializer for the Dog class. // Whenever the Dog class is encountered Gson calls the DogSerializer // we set pretty printing own to format the json Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogSerializer()).setPrettyPrinting().create(); // Since Animal contains generic type create the type using TypeToken // class. Type animalType = new TypeToken<Animal<Dog>>() { }.getType(); System.out.println(gson.toJson(animal, animalType)); } } // The Animal class public class Animal<t> { public T animal; public void setAnimal(T animal) { this.animal = animal; } public T get() { return animal; } } // The Dog class public class Dog { private String name; public Dog(String name) { this.name = name; } public String getName() { return name; } } // Custom Deserialize public class DogDeserialiser implements JsonDeserializer<Dog> { @Override public Dog deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String name = json.getAsJsonObject().get("name").getAsString(); name = name.replace(" ", "_"); Dog dog = new Dog(name); return dog; } public static void main(String[] args) { String json = "{\"animal\":{\"name\":\"I am a dog\"}}"; Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogDeserialiser()).create(); Type animalType = new TypeToken<Animal<Dog>>() { }.getType(); Animal<Dog> animal = gson.fromJson(json, animalType); System.out.println(animal.get().getName()); }}Copy the code
#### Write the instance creator
When deserializing an object,Gson creates a default instance of the class that is required. A well-written class means having the no-argument constructors (which can be public or private) needed for serialization and deserialization. In general, instance creators can be used if a class does not have a no-argument constructor. Such as:
private class MoneyInstanceCreator implements InstanceCreator<Money> { public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); }}Copy the code
The type here can be the corresponding generic type. 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(Type type) { // No need to use a parameterized list since the actual instance will have the raw type anyway. return new 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(Type type) { Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); Type idType = typeParameters[0]; // Id has only one parameterized type T return Id.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 the passed method parameter type. In this case, the Type object is the Id of the location to which the actual instance of the Java parameterized type representation Id should be bound. Since the Id class 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.
Compact VS JSON’s beautiful output format
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 is ignored in the output.
To use the “Pretty Print” feature, Gson must configure the instance GsonBuilder using “Configure”. Gson doesn’t have a public API that exposes JsonFormatter, so the client can’t configure JSON output from the default Print Settings/Margins. 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 implementation of Gson ignores null values. However, when you use Gson and want to add a default value for this field, you can configure Gson as follows:
Gson gson = new GsonBuilder().serializeNulls().create();
Copy the code
Note: This configuration adds a JsonNull element to the JsonElement when Gson serializes null. Therefore, this value can be used in custom serialization and deserialization. Such as:
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
Versioning 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. Such as:
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(versionedObject); System.out.println(jsonOutput); System.out.println(); gson = new Gson(); jsonOutput = gson.toJson(versionedObject); System.out.println(jsonOutput);Copy the code
The output is:
{"newField":"new","field":"old"}
{"newerField":"newer","newField":"new","field":"old"}
Copy the code
Exclude fields from serialization and deserialization
Gson provides a number of mechanisms for excluding top-level classes, fields, and field types. The following is a flexible mechanism that allows 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 excludeFieldsWithModifiers method with an arbitrary number of Modifier constant. Such as:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
.create();
Copy the code
- Gson @ Expose
You can use the @expose annotation to exclude fields that you don’t want to be serialized or deserialized by Gson. But Gson instance must through new GsonBuilder () excludeFieldsWithoutExposeAnnotation (). The create (). To create. Gson will then exclude fields that annotate comments. 3. If none of the above mechanics work for you, write your own elimination strategy and add it to Gson. Refer to the ExclusionStrategy documentation. Here are some examples:
@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); }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 (that is, the hump name starting with a lowercase letter, sampleFieldNameInJava) to the Json field name (that is, sample_field_name_in_java or sampleFieldNameInJava). For information about predefined naming policies, see the FieldNamingPolicy class. Such as:
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
Custom serialization and deserialization shared state
Sometimes you need to share state between custom serializers/deserializers. Three strategies can be used to accomplish this task:
- 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.