Gson of Java source code analysis

Time:2021-6-11

1: To the official website, the main function of gson is the mutual conversion between Java objects and JSON. Let’s take a look at the simplest example

@AllArgsConstructor
public static class Book{
 private Integer id;
 private String name;
}

public static void main(String[] args) {
 Map<String,Book> map = Maps.newHashMap();
 map.put("1", new Book(1,"1"));
 map.put("2", new Book(2,"2"));
 map.put("3", new Book(3,"3"));
 final Gson gson = new Gson();
 final String json = gson.toJson(map);
 System.out.println(json);
 
 final Map map1 = gson.fromJson(json, new    TypeToken<Map<String,Book>>(){}.getType());
 System.out.println(map1);
 
}
#Output results
{"1":{"id":1,"name":"1"},"2":{"id":2,"name":"2"},"3":{"id":3,"name":"3"}}
{[email protected], [email protected], [email protected]}

It can be seen that Java object is converted to JSON string, and JSON string is also reversed to Java object
But the most worthy research of gson is how he knows to convert to the object structure of map < string, book > in the process from JSON self string to Java object object? What is the theory of the whole transformation?

2: Let’s first look at what typetoken is?

Map map1 =  gson.fromJson(json, new TypeToken<Map<String,Book>>(){}.getType());

By analyzing the above code, new typetoken < map < string, book > > () {}. Gettype() instantiates an anonymous inner class object and calls the gettype() method of the anonymous inner class object
To know what the GetType () returns, we have to look at the source code in the typetoken class

#Class declaration
public class TypeToken<T> {
 final Class<? super T> rawType;
 final Type type;
 final int hashCode;

#Constructors
@SuppressWarnings("unchecked")
protected TypeToken() {
 this.type = getSuperclassTypeParameter(getClass());
 this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
 this.hashCode = type.hashCode();
}

public final Type getType() {
 return type;
}

It can be seen that typetoken is a common generic class, and the type variable holds the parameterized type object. See the figure below. In fact, type is parameterized type object, and the actual implementation class is sun.reflect.generics.reflectiveobjects.parameterized typeimpl

Through this object, we can get the actual object of the generic,
p.getActualTypeArguments();  
p.getRawType()

Through the above two methods, you can obtain the actual type of generic type, namely:
java.util.Map<java.lang.String,com.company.gson.Main$Book>
In fact, it is the Java object we want to convert from the anonymous inner class
Gson of Java source code analysis
Digression: to figure out the parameterized type, take a look at the series of type interfaces in Java
Here: we can come to the conclusion that we actually need to tell gson what the type is after deserialization. Here, we pass in the generic type through anonymous inner class. Gson obtains the actual type through parameterizedtype [see the method of parameterizedtype interface]
3: Continue to analyze the source code

public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
 JsonReader jsonReader = newJsonReader(json);
 T object = (T) fromJson(jsonReader, typeOfT);
 assertFullConsumption(object, jsonReader);
 return object;
}

The input parameters are JSON character and typeoft [actually parameterized type object, you can get the actual type of generic type]. So the core deserialization code enters the

T object = (T) fromJson(jsonReader, typeOfT);

Gson of Java source code analysis

Just look at the core code

1: The first step
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
2: Step two
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
3: The third step
T object = typeAdapter.read(reader);

Step 1: return another typetoken object through typetoken.get (typeoft). You can go in here to see if there is any relationship between our anonymous inner class objects. The code is as follows:

/**
 * Gets type literal for the given {@code Type} instance.
 */public static TypeToken<?> get(Type type) {
 return new TypeToken<Object>(type);
}

You can see that you directly create a typetoken object and pass in the type directly

Step 2: return a typeadapter object through the typetoken object to see the code directly
Gson of Java source code analysis

It can be seen that the core code is list < typeadapterfactory > factories;
Traverse factories and call each factory method, pass in gson object and type to generate the corresponding typeadapter
So far: the core idea of gson is to generate different typeadapters according to different types
There are so many factory objects at present
Gson of Java source code analysis
My example uses maptypeadapterfactory
Gson of Java source code analysis
Find the initialization place of factors

List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
// the excluder must precede all adapters that handle user-defined types
factories.add(excluder);
// user's type adapters
factories.addAll(typeAdapterFactories);
// type adapters for basic platform types
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.BYTE_FACTORY);
factories.add(TypeAdapters.SHORT_FACTORY);
TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
factories.add(TypeAdapters.newFactory(double.class, Double.class,
 doubleAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.newFactory(float.class, Float.class,
 floatAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.NUMBER_FACTORY);
factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
factories.add(TypeAdapters.CHARACTER_FACTORY);
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
factories.add(TypeAdapters.URL_FACTORY);
factories.add(TypeAdapters.URI_FACTORY);
factories.add(TypeAdapters.UUID_FACTORY);
factories.add(TypeAdapters.CURRENCY_FACTORY);
factories.add(TypeAdapters.LOCALE_FACTORY);
factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
factories.add(TypeAdapters.BIT_SET_FACTORY);
factories.add(DateTypeAdapter.FACTORY);
factories.add(TypeAdapters.CALENDAR_FACTORY);
factories.add(TimeTypeAdapter.FACTORY);
factories.add(SqlDateTypeAdapter.FACTORY);
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(TypeAdapters.CLASS_FACTORY);
// type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
factories.add(jsonAdapterFactory);
factories.add(TypeAdapters.ENUM_FACTORY);
factories.add(new ReflectiveTypeAdapterFactory(
 constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
this.factories = Collections.unmodifiableList(factories);

We can see that gson enumerates all possible factories, generates the corresponding typeadapter according to the type of our deserialization, and then completes the deserialization
Step 3: turn JSON into Java object object through typeadapter