• Gson is a library we often use to map Between Java objects and JSON data. Today we will talk about its use and source code analysis separately

The use of,

1. Basic parsing and generation

  • Gson provides two direct parsing and generation methods fromJson() and toJson(), the former for deserialization and the latter for serialization
  1. Parse the base data types
Val gson = gson () val I = gson.fromjson ("100", Int::class.java) //100 val d = gson.fromjson ("99.99", Double::class.java) //99.99 val b = gson.fromjson ("true", Boolean::class.java) // true val s = gson.fromjson ("jin", String::class.java) // String log("i=$i,d=$d,b=$b,s=$s")Copy the code
  1. Generate base data types
Val jsonI: String = gson.tojson (100) // 100 val jsonD: String = gson.tojson (99.99) // 100 val jsonB: String = gson. String = gson.toJson(false) // false val jsonS: String = gson.toJson("String") //"String" log("jsonI=$jsonI,jsonD=$jsonD,jsonB=$jsonB,jsonS=$jsonS")Copy the code
  1. Parses and generates custom objects
Data class UserInfo(var name: String?); // Define a data model class. , var age: Int? , var emailAddress: String? ) Val user = UserInfo(name = "222", age = 18, "[email protected]") log("user=${user}") // convert toJson string val gson = gson () val userStr = gson.tojson (user) log("userStr:$userStr") Val user2: UserInfo? = gson.fromjson (userStr, UserInfo::class.java) log("user2=${user2}")Copy the code

2. Rename the attribute

Use of @serializedName annotations
  • @serializedName takes two arguments, the value string renames the property, and the alternate array provides multiple alternative property names for the property
  • The property name in the string after toJson will take the value in value
  • Provide alternative attribute names for fields, using the last value to occur when more than one case occurs simultaneously
  1. Add annotations to the data model class
data class UserInfo( @SerializedName(value = "name", alternate = ["user_name"]) var name: String? , @SerializedName("age", alternate = ["user_age", "u_age", "Age"]) var age: Int? , @SerializedName("emailAddress") var emailAddress: String? = null, )Copy the code
  1. Parse and generate JSON strings
val user3: UserInfo? =
    gson.fromJson("{\"user_age\":18,\"user_name\":\"333\"}", UserInfo::class.java)
log("user3=$user3")
val user4: UserInfo? =
    gson.fromJson("{\"u_age\":18,\"user_name\":\"444\"}", UserInfo::class.java)
log("user4=$user4")
val user5: UserInfo? = gson.fromJson(
    "{\"Age\":18,\"user_age\":19,\"u_age\":20,\"user_name\":\"555\"}",
    UserInfo::class.java
)
log("user5=$user5")
Copy the code
Field mapping rules for POJO and JSON
  • GsonBuilder provides two methods: setFieldNamingPolicy and setFieldNamingStrategy.
  1. The setFieldNamingPolicy method, used in conjunction with the FieldNamingPolicy enumeration, offers the following options
val gson = GsonBuilder()
//  .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)//emailAddress
//  .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)//email-address
//  .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)//email_address
//  .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//EmailAddress
    .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES)//Email Address
    .create()
val user = UserInfo(name = "222", age = 18, "[email protected]")
log("setFieldNamingPolicy:${gson.toJson(UserInfo3())}")
Copy the code
  1. SetFieldNamingStrategy implements its own rules with the FieldNamingStrategy interface provided by Gson
FieldStrategy {** ** all uppercase */ class AllUpperCaseStrategy: FieldNamingStrategy { override fun translateName(f: Field): String {return f.name.upperCase (locale.getdefault ())}} /** * Add the first and second annotations */ class GsonKeyValueCaseStrategy: FieldNamingStrategy { override fun translateName(f: Field): String { val gkv: GsonKeyValue? = f.getAnnotation(GsonKeyValue::class.java) return if (gkv ! = null) { if (gkv.value.isNotEmpty()) { gkv.prefix + gkv.value + gkv.suffix } else { gkv.prefix + f.name + gkv.suffix } } else { f.name } } } } //2. Use the val gson = GsonBuilder (.) setFieldNamingStrategy (FieldStrategy. AllUpperCaseStrategy ()) .setFieldNamingStrategy(FieldStrategy.GsonKeyValueCaseStrategy()) .create() val user = UserInfo(name = "222", age = 18, "[email protected]") log("setFieldNamingStrategy:${gson.toJson(user)}")Copy the code
  • The @SerializedName annotation has the highest precedence, and FieldNamingStrategy does not take effect on fields annotated with @SerializedName

Using GsonBuilder

  • Instead of using the Gson() constructor to create a Gson instance, we used GsonBuilder, some of which are described below
Val gsonDIY = GsonBuilder() // serialize null. SerializeNulls () // set date-time format, Another two overloaded methods / / were to take effect at the time of serialization and reverse ordering. SetDateFormat (" yyyy - MM - dd ") / / ban this serialization inner classes. DisableInnerClassSerialization () / / generate non-executable Json, Is actually much more)]} '. This four characters generateNonExecutableJson () / / ban escaped HTML tags. DisableHtmlEscaping () / / formatting output setPrettyPrinting () .create() // generate the configured Gson // use val user8 = UserInfo("888", 18) val user8Str = gsonDIY.toJson(user8) log("user8 toJson:${user8Str}") log("fromJson user8Str:${gsonDIY.fromJson(user8Str, UserInfo::class.java)}")Copy the code

The generic

val gson = Gson() val jsonArray = gson.toJson(arrayOf(user2, user3, user4, user5)) val userArr = gson.fromJson(jsonArray, Array<UserInfo>::class.java) log("userArr=${gson.toJson(userArr)}") val userList: List<UserInfo> = gson.fromJson(jsonArray, List::class.java) as List<UserInfo> log("userList=$userList") // Gson provides TypeToken support for generics with val userList2: List<UserInfo?>? = gson.fromJson(jsonArray, object : TypeToken<List<UserInfo?>?>() {}.type) log("userList2=$userList2")Copy the code
Encapsulation of generics
  • But writing TypeToken every time a generic is parsed is cumbersome, so we can do some simple encapsulation in our utility classes
fun getListType(type: Type): Type {
    return TypeToken.getParameterized(MutableList::class.java, type).type
}

fun getSetType(type: Type): Type {
    return TypeToken.getParameterized(MutableSet::class.java, type).type
}

fun getMapType(keyType: Type, valueType: Type): Type {
    return TypeToken.getParameterized(MutableMap::class.java, keyType, valueType).type
}

fun getArrayType(type: Type): Type {
    return TypeToken.getArray(type).type
}

fun getType(rawType: Type, vararg typeArguments: Type): Type {
    return TypeToken.getParameterized(rawType, *typeArguments).type
}
Copy the code
  • Or it can involve real business, such as network data parsing, which can be further encapsulated as follows
/ / fun <T> fromJson2Object(json: String? , clazz: Class<T>): BaseHttpResponse<T>? { val type: Type = getType(BaseHttpResponse::class.java, clazz) return fromJson(json, */ fun <T> fromJson2Array(json: String? , clazz: Class<T>): BaseHttpResponse<MutableList<T? >? >? {// Generate BaseHttpResponse<List<T> val Type: clazz; // generate BaseHttpResponse<List<T> val Type: Type = getType (BaseHttpResponse: : class. Java, listType) return fromJson (json, Type)} / analytical Map data is * * * * / fun < K, T> fromJson2Map(json: String? , clazz1: Class<K>, clazz2: Class<T>): BaseHttpResponse<MutableMap<K? , T? >? >? {// Generate BaseHttpResponse<Map<K,T>> Map<K,T> val mapType: Type = getMapType(clazz1, clazz2); Type = getType(BaseHttpResponse::class.java, mapType) return fromJson(json, type) }Copy the code
  • The usage tests encapsulated above are as follows
// BaseHttpResponse<T>(@serializedName (value = "code", alternate = ["Status", "Status"]) Int? = null, @SerializedName(value = "message", alternate = ["Msg", "msg"]) var message: String? = null, @SerializedName(value = "data", alternate = ["Data"]) var data: T? Val baseHttpResponse1=BaseHttpResponse(111,"aaa",UserInfo3("ljy",18)) val strResponse1:String=GsonUtils.toJson(baseHttpResponse1) The log (" fromJsonObject: ${GsonUtils. FromJson2Object (strResponse1 UserInfo3: : class. Java)} ") / / on the List < UserInfo3 > analytic val list=mutableListOf(UserInfo3("ljy",18),UserInfo3("qwer",19)) log("fromJson(json,Type):${GsonUtils.fromJson<List<UserInfo3>>(GsonUtils.toJson(list),GsonUtils.getListType(UserInfo3::c Lass.java))}") // BaseHttpResponse<List<UserInfo3>> val baseHttpResponse2=BaseHttpResponse<List<UserInfo3>>(111,"aaa",list) val strResponse2:String=GsonUtils.toJson(baseHttpResponse2) The log (" fromJsonArray: ${GsonUtils. FromJson2Array (strResponse2 UserInfo3: : class. Java)} ") / / the Map < String, UserInfo3 > analytic val map= mutableMapOf("key1" to UserInfo3("ljy",18),"key2" to UserInfo3("qwer",19)) log("fromJson(json,Type):${GsonUtils.fromJson<Map<String,UserInfo3>>(GsonUtils.toJson(map),GsonUtils.getMapType(String:: Class. Java, UserInfo3: : class. Java))} ") / / to BaseHttpResponse < Map < String, UserInfo3 > > analytic val baseHttpResponse3=BaseHttpResponse<List<UserInfo3>>(111,"aaa",map) val strResponse3:String=GsonUtils.toJson(baseHttpResponse3) log("fromJsonMap:${GsonUtils.fromJson2Map(strResponse3,String::class.java,UserInfo3::class.java)}")Copy the code

Serialization and deserialization of Gson

Streaming deserialization of Gson
  1. Manual: Use the Stream JsonReader class to manually deserialize the stream, similar to the Android pull XML parsing method
val user6 = UserInfo(null, null, null, null) val reader = JsonReader(StringReader(userStr)) reader.beginObject() while (reader.hasNext()) { when (reader.nextName()) { "name" -> user6.name = reader.nextString() "age" -> user6.age = reader.nextInt() "emailAddress" ->  user6.emailAddress = reader.nextString() "temp" -> user6.temp = reader.nextString() "date" -> user6.date = Date(reader.nextString()) } } reader.endObject() log("user6=$user6")Copy the code
  1. If the first argument is a String, then Gson creates a StringReader to stream.
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
}
Copy the code
Stream serialization of Gson
  1. manual
val user7 = UserInfo("777", 17, "[email protected]") // val writer = JsonWriter(OutputStreamWriter(System.out)) val stringWriter = StringWriter() val writer =  JsonWriter(stringWriter) writer.beginObject() .name("name").value(user7.name) .name("age").value(user7.age) .name("emailAddress").nullValue() .endObject() writer.flush() log("user7=$stringWriter")Copy the code
  1. automatic
PrintStream(system.out), StringBuilder, StringBuffer, and *Writer all implement Appendable. Gson.tojson (user, system.out) val str1 = gson.tojson (user) //toJson  //public String toJson(Object src, Type typeOfSrc) { // StringWriter writer = new StringWriter(); // toJson(src, typeOfSrc, writer); // return writer.toString(); / /}Copy the code

Several methods of field filtering

1. @ Expose annotation
  • Based on the @expose annotation: Fields that need to be exported are annotated with the @expose annotation, but fields that do not need to be exported are not
Data class UserInfo(@expose // @expose provides two properties and defaults to true var name: String? , @expose (deserialize = true,serialize = true) , @expose (deserialize = true,serialize = false) // = null, @expose (deserialize = false,serialize = true) var date: date? = Date(), @expose (deserialize = false,serialize = false) // = "useless temp")Copy the code
  • When using Gson also want to use GsonBuilder call excludeFieldsWithoutExposeAnnotation method to support @ x.
val gson3 = GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create()
gson3.toJson(user8)
log("user8 toJson:${gson3.toJson(user8)}")
Copy the code
2. Based on version
  • Gson provides two annotations for version-based field exports, @since and @until, both of which receive a Double value
data class UserInfo2( var name: String? , @since (3.0) var age: Int? , @until (6.0) var emailAddress: String? = null, )Copy the code
  • Used with gsonBuilder.setVersion (Double), this field is exported when the current version (the version set in GsonBuilder) is greater than or equal to the value of Since or less than the value of Until
val user9 = UserInfo2("999", 19, "[email protected]") log("user9 version 1.0:" + GsonBuilder().setVersion(1.0).create().tojson (user9)) log("user9 version 4.0:") + GsonBuilder().setVersion(4.0).create().tojson (user9)) log("user9 Version 9.0:" + GsonBuilder (.) setVersion (9.0). The create (). The toJson (user9)) val user9Str = 19, "{\" age \ ": \" emailAddress \ ": \" 9999 @qq.com \ ", \ "name \" : \ "999 \"} "log (" user9Str version 1.0:" + GsonBuilder (.) setVersion (1.0). The create () fromJson (user9Str, UserInfo2::class.java)) log("user9Str version 4.0:" + GsonBuilder().setVersion(4.0).create().fromjson (user9Str, UserInfo2::class.java)) log("user9Str version 9.0:" + GsonBuilder().setVersion(9.0).create().fromjson (user9Str, UserInfo2::class.java))Copy the code
3. Based on access modifiers
  • Based on the public, the static, final, private and protected the access modifier, through GsonBuilder () excludeFieldsWithModifiers () method to control
Public class Test {final String finalField = "final"; static String staticField = "static"; public String publicField = "public"; protected String protectedField = "protected"; String defaultField = "default"; boolean man=true; private String privateField = "private"; } / / out of final, static, private modified field val gson4 = GsonBuilder () excludeFieldsWithModifiers (Modifier. Final, Modifier, the static, Modifier.PRIVATE) .create() log(gson4.toJson(Test()))Copy the code
4. Policy-based (custom rules)
  • GsonBuilder addSerializationExclusionStrategy, addDeserializationExclusionStrategy two methods respectively for serialization and ordering
  • The two methods pass in the implementation class of the ExclusionStrategy interface, where they implement their own rules
val gson5 = GsonBuilder() .addSerializationExclusionStrategy(object : ExclusionStrategy { override fun shouldSkipField(f: FieldAttributes): Boolean {// make a judgment here, If ("emailAddress" == f.name) return true // Exclude val expose = by field name f.getAnnotation(Expose::class.java) return expose ! = null && ! Override fun shouldSkipClass(clazz: Class<*>): Boolean {// Directly exclude a class, Return true to rule out, for example, to exclude Boolean. Kt and Boolean. Java return clazz = = Boolean: : class | | clazz = = Boolean: : class. Java}}). The create ()  log("UserInfo3:${gson5.toJson(UserInfo3())}") log("Test:${gson5.toJson(Test())}")Copy the code

TypeAdapter

  • Used to take over the serialization and deserialization processes of a certain type, consisting of two critical methods write(JsonWriter,T) and read(JsonReader). All other methods are final and ultimately call these two abstract methods.
  1. Custom UserInfoTypeAdapter
class UserInfoTypeAdapter : TypeAdapter<UserInfo3? >() { @Throws(IOException::class) override fun write(out: JsonWriter? , value: UserInfo3?) { out? .run { value?.let { beginObject() name("name").value(it.name) name("age").value(it.age) name("emailAddress").value(it.emailAddress) endObject() } } } @Throws(IOException::class) override fun read(`in`: JsonReader): UserInfo3 { val user = UserInfo3() `in`.beginObject() while (`in`.hasNext()) { when (`in`.nextName()) { "name" -> user.name = `in`.nextString() "age" -> user.age = `in`.nextInt() "email", "email_address", "emailAddress" -> user.emailAddress = `in`.nextString() } } `in`.endObject() return user } }Copy the code
  1. Register UserInfoTypeAdapter for UserInfo3
val gson8 = GsonBuilder()
    .registerTypeAdapter(UserInfo3::class.java, UserInfoTypeAdapter())
    .create()
log(gson8.toJson(UserInfo3()))
log(gson8.fromJson("{\"name\":\"ljy\",\"age\":20,\"email_address\":\"[email protected]\",\"email\":\"[email protected]\"}", UserInfo3::class.java))
Copy the code

JsonSerializer and JsonDeserializer

  • But what if you just want to serialize or deserialize one of them? Use JsonSerializer only for serialization and JsonDeserializer only for deserialization
  • For example, if an interface returns a field of type int, what if it returns an empty string
  1. Use TypeAdapter implementation
class NumTypeAdapter : TypeAdapter<Number>() { override fun write(out: JsonWriter, value: Number) { log("numTypeAdapter.write:$value") out.value(value.toString()) } override fun read(`in`: JsonReader): Number { return try { val str = `in`.nextString() log("numTypeAdapter.read:$str") if (str == null || str.isEmpty()) { return -1 } NumberFormat.getInstance().parse(str) ? : -1 } catch (e: Exception) { log("e:$e") -1 } } }Copy the code
  1. JsonSerializer and JsonDeserializer are used
class NumJsonSerializer : JsonSerializer<Number> { override fun serialize( src: Number? , typeOfSrc: Type? , context: JsonSerializationContext? ) : JsonElement { log("typeOfSrc:$typeOfSrc") return JsonPrimitive(src.toString()) } } class NumJsonDeserializer : JsonDeserializer<Number> { override fun deserialize( json: JsonElement? , typeOfT: Type? , context: JsonDeserializationContext? ) : Number { return try { log("typeOfT:$typeOfT") json? .asNumber ? : -1 } catch (e: Exception) { log("e:${e}") -1 } } }Copy the code
  1. Use the test
val gson9 = GsonBuilder() .registerTypeAdapter(Number::class.java, NumTypeAdapter()) .registerTypeHierarchyAdapter(Number::class.java, NumJsonSerializer()) .registerTypeHierarchyAdapter(Number::class.java, NumJsonDeserializer()).create() log(gson9.tojson (100, Number::class.java)) log(gson9.tojson (6.66, Number::class.java)) log(gson9.toJson(111111111111111111, Number::class.java)) log(gson9.fromJson("\"\"", Number: : class. Java)) log (gson9. FromJson (" ", Number: : class. Java)) log (gson9. FromJson (" 88.88 ", Number::class.java)) log(gson9.fromJson("55", Number::class.java)) log(gson9.fromJson("111111111111111111", Number: : class. Java)) log (gson9. ToJson (100, Int: : class. Java)) log (gson9. ToJson (6.66, Double::class.java)) log(gson9.toJson(111111111111111111, Long::class.java)) log(gson9.fromJson("\"\"", Number: : class. Java)) log (gson9. FromJson (", "Double: : class. Java)) log (gson9. FromJson (" 88.88", Double::class.java)) log(gson9.fromJson("55", Int::class.java)) log(gson9.fromJson("111111111111111111", Long::class.java))Copy the code
  • The difference between the registerTypeAdapter and registerTypeHierarchyAdapter:
    • RegisterTypeAdapter: Supports generics, not inheritance
    • RegisterTypeHierarchyAdapter: does not support generics, support inheritance
  • Compared with JsonSerializer and JsonDeserializer, TypeAdapter provides high efficiency, small memory usage, and supports the Stream API
In actual combat
  • In the data returned by the server, the type of data field is not fixed. For example, the data is a List when the request is successful, and the type of data field is String when the request is unsuccessful.
val gson11 = GsonBuilder().registerTypeHierarchyAdapter( MutableList::class.java, JsonDeserializer { json, typeOfT, context -> if (json.isJsonArray) { log("isJsonArray") val array = json.asJsonArray val itemType: Type = (typeOfT as ParameterizedType).actualTypeArguments[0] val list: MutableList<Any? > = mutableListOf() for (i in 0 until array.size()) { val element = array[i] val item = context.deserialize<Any>(element, ItemType) list.add(item)} list} else {log(" Return empty list ") collections.empty_list}}).create() log(gson11.toJson(listOf(UserInfo3()))) val type = object : TypeToken<List<UserInfo3?>?>() {}.type log(gson11.fromJson("[{\"age\":20,\"emailAddress\":\"[email protected]\",\"height\":180,\"man\":true,\"name\":\"ljy\"}]", Type)) log(gson11.fromjson ("{}", type))Copy the code

TypeAdapterFactory

  • Used to create TypeAdapter factory class, by comparing the Type, sure there is no corresponding TypeAdapter, no returns null, and GsonBuilder. RegisterTypeAdapterFactory cooperate to use
  • For example, if it is null, return “” if it is String, or empty List if it is List
class GsonDefaultAdapterFactory: TypeAdapterFactory { override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { if (type.type == String::class.java) { return createStringAdapter() } if (type.rawType == List::class.java || type.rawType == Collection::class.java) { return createCollectionAdapter(type, List */ private fun <T: Any> createCollectionAdapter(type: TypeToken<T>, gson: Gson ): TypeAdapter<T>? { val rawType = type.rawType if (! Collection::class.java.isAssignableFrom(rawType)) { return null } val elementType: Type = `$Gson$Types`.getCollectionElementType(type.type, rawType) val elementTypeAdapter: TypeAdapter<Any> = gson.getAdapter(TypeToken.get(elementType)) as TypeAdapter<Any> return object : TypeAdapter<Collection<Any>>() { override fun write(writer: JsonWriter, value: Collection<Any>?) { writer.beginArray() value? .forEach { elementTypeAdapter.write(writer, it) } writer.endArray() } override fun read(reader: JsonReader): Collection<Any> {val list = mutableListOf<Any>() // null replace "" if (reader.peek() == jsontoken.null) {reader.nextnull () return list } reader.beginArray() while (reader.hasNext()) { val element = elementTypeAdapter.read(reader) List.add (element)} reader.endarray () return list}} as TypeAdapter<T>} private fun <T: Any> createStringAdapter(): TypeAdapter<T> { return object : TypeAdapter<String>() { override fun write(writer: JsonWriter, value: String?) { if (value == null) { writer.value("") } else { writer.value(value) } } override fun read(reader: JsonReader): String {// null replace with "" if (reader.peek() == jsontoken.null) {reader.nextnull () return ""} return reader.nextString()} } as TypeAdapter < T >}} / / the val gson10 = GsonBuilder () registerTypeAdapterFactory (GsonDefaultAdapterFactory ()) .create()Copy the code

@ JsonAdapter annotations

  • To use on the custom model class, receives a parameter, and the parameter types must be TypeAdapter TypeAdapterFactory, JsonSerializer or JsonDeserializer one of them
//  @JsonAdapter(NumJsonSerializer::class)
//  @JsonAdapter(NumJsonDeserializer::class)
//  @JsonAdapter(NumTypeAdapter::class)
@JsonAdapter(KotlinAdapterFactory2::class)
data class UserInfo5(
    var name: String = "ljy",
    private var email: String? = "[email protected]",
    private val mobile: String? = "12315",
)
Copy the code
  • There is no need to use GsonBuilder to register UserTypeAdapter
log("UserInfo5:${Gson().toJson(UserInfo5())}")
Copy the code

The source code parsing

  • We started with the most basic methods fromJson and toJson, so let’s click through and see how they work

FromJson method

  • FromJson has multiple overloaded methods that eventually call fromJson(JsonReader reader, Type typeOfT)
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {//Class<T> converts to Type Object Object = fromJson(json, (Type) classOfT); return Primitives.wrap(classOfT).cast(object); } public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException { if (json == null) { return null; StringReader StringReader = new StringReader(json); T target = (T) fromJson(reader, typeOfT); return target; } public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {// Wrap Reader as JsonReader JsonReader JsonReader = newJsonReader(json); T object = (T) fromJson(jsonReader, typeOfT); assertFullConsumption(object, jsonReader); return object; } public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; boolean oldLenient = reader.isLenient(); reader.setLenient(true); try { reader.peek(); isEmpty = false; TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT); TypeAdapter<T> TypeAdapter = getAdapter(typeToken); T object = typeAdapter.read(reader); return object; } catch //... } finally {reader.setlenient (oldLenient); }}Copy the code
  • As you can see from the above code, getAdapter is called to obtain the typeToken from the typeToken, and then the typeAdapter. read method is used to deserialize the data. So the read method is not implicit;

GetAdapter method

  • So how is the getAdapter method implemented
Public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {ConcurrentHashMap<TypeToken<? >, TypeAdapter<? >> typeTokenCache fetch TypeAdapter<? > cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); if (cached ! = null) { return (TypeAdapter<T>) cached; ThreadLocal<Map<TypeToken<? >, FutureTypeAdapter<? >>> Calls fetch Map<TypeToken<? >, FutureTypeAdapter<? >> threadCalls = calls.get(); boolean requiresThreadLocalCleanup = false; if (threadCalls == null) { threadCalls = new HashMap<TypeToken<? >, FutureTypeAdapter<? > > (); calls.set(threadCalls); requiresThreadLocalCleanup = true; } // the key and value type parameters always agree FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type); if (ongoingCall ! = null) { return ongoingCall; } try { FutureTypeAdapter<T> call = new FutureTypeAdapter<T>(); threadCalls.put(type, call); <TypeAdapterFactory> Factories for (TypeAdapterFactory factory: factories) { TypeAdapter<T> candidate = factory.create(this, type); if (candidate ! = null) { call.setDelegate(candidate); typeTokenCache.put(type, candidate); return candidate; } } throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type); } finally { threadCalls.remove(type); if (requiresThreadLocalCleanup) { calls.remove(); }}}Copy the code
  • ThreadLocal caches TypeAdapter objects, and different threads use the cache to resolve each other
  • The Gson constructor (” Gson “, “Gson”, “Gson”, “Gson”, “Gson”, “Gson”, “Gson”, “Gson”

Gson constructor

public Gson() { this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, Collections.<Type, InstanceCreator<? >>emptyMap(), DEFAULT_SERIALIZE_NULLS, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList()); } Gson(...) {... List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(); factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); factories.add(ObjectTypeAdapter.FACTORY); factories.add(excluder); factories.addAll(factoriesToBeAdded); 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); Factories. The add... Too much / / 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); }Copy the code
  • You can see that the STRING_FACTORY in the Factories adds a lot of prefabricated TypeAdapterFactory
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);

public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
    @Override
    public String read(JsonReader in) throws IOException {
      JsonToken peek = in.peek();
      if (peek == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      /* coerce booleans to strings for backwards compatibility */
      if (peek == JsonToken.BOOLEAN) {
        return Boolean.toString(in.nextBoolean());
      }
      return in.nextString();
    }
    @Override
    public void write(JsonWriter out, String value) throws IOException {
      out.value(value);
    }
};
Copy the code
  • GetRawType () == type, returns the TypeAdapter STRING of the input parameter
public static <TT> TypeAdapterFactory newFactory( final Class<TT> type, final TypeAdapter<TT> typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null; } @Override public String toString() { return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; }}; }Copy the code
  • Back to the getAdapter method, the factory. Create method is called in the loop Factories, which is the TypeAdapterFactory create method implemented in newFactory above
  • One thing to note is the order in which Gson initializes the Factories, starting with the TypeAdapterFactory of the various commonly used base classes, In the last few lines add JsonAdapterAnnotationTypeAdapterFactory object before ReflectiveTypeAdapterFactory object, If the entity class using the @ JsonAdapter and specify the adapter in the then returns @ JsonAdapter specified adapter and does not return ReflectiveTypeAdapterFactory created, so we can take over the back of the parsing process
JsonAdapterAnnotationTypeAdapterFactory
  • The create method JsonAdapterAnnotationTypeAdapterFactory are as follows
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
    Class<? super T> rawType = targetType.getRawType();
    JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
    if (annotation == null) {
      return null;
    }
    return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
}
Copy the code
ReflectiveTypeAdapterFactory
  • No use @ JsonAdapter comment return ReflectiveTypeAdapterFactory instance, we see the create method of it
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) { Class<? super T> raw = type.getRawType(); if (! Object.class.isAssignableFrom(raw)) { return null; // it's a primitive! } ObjectConstructor<T> constructor = constructorConstructor.get(type); return new Adapter<T>(constructor, getBoundFields(gson, type, raw)); }Copy the code
  • Create calls the getBoundFields method to add fields from the entity class that need to be resolved to a collection and assign values during deserialization
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<? > type, Class<? > raw) { Map<String, BoundField> result = new LinkedHashMap<String, BoundField>(); if (raw.isInterface()) { return result; } Type declaredType = type.getType(); while (raw ! = object.class) {Field[] fields = raw.getDeclaredFields(); Boolean serialize = excludeField(Field, true); for (Field Field: fields) {// Whether fields participate in deserialization or serialization process Boolean serialize = excludeField(Field, true); boolean deserialize = excludeField(field, false); if (! serialize && ! deserialize) { continue; } accessor.makeAccessible(field); Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); List<String> fieldNames = getFieldNames(field); BoundField previous = null; for (int i = 0, size = fieldNames.size(); i < size; ++i) { String name = fieldNames.get(i); if (i ! = 0) serialize = false; // only serialize the default name // BoundField boundField = createBoundField(context, field, name, TypeToken.get(fieldType), serialize, deserialize); BoundField replaced = result.put(name, boundField); if (previous == null) previous = replaced; } if (previous ! = null) { throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name); } } type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); raw = type.getRawType(); } return result; }Copy the code
  • ExcludeField is called to determine whether a field participates in deserialization or serialization, as follows
public boolean excludeField(Field f, boolean serialize) { return excludeField(f, serialize, excluder); } static boolean excludeField(Field f, boolean serialize, Excluder excluder) { return ! excluder.excludeClass(f.getType(), serialize) && ! excluder.excludeField(f, serialize); } excludeClassChecks whether class types meet the requirements for serialization or deserialization, using the Since and Until annotations // Excluder. ExcludeField (f, serialize) Public Boolean excludeClass(Class<? > clazz, boolean serialize) { return excludeClassChecks(clazz) || excludeClassInStrategy(clazz, serialize); }Copy the code
  • Back in getBoundFields, it calls createBoundField to create the BoundField
createBoundField
private ReflectiveTypeAdapterFactory.BoundField createBoundField( final Gson context, final Field field, final String name, final TypeToken<? > fieldType, boolean serialize, Boolean deserialize) {final Boolean isPrimitive = primitive.isprimitive (fieldtype.getrawtype ()); // special casing primitives here saves ~5% on Android... // @jsonAdapter annotation. If an attribute of an entity class uses @jsonAdapter, the serialization and deserialization of that attribute will be taken over by the specified adapter. JsonAdapter annotation = field.getannotation (jsonAdapter.class); TypeAdapter<? > mapped = null; if (annotation ! = null) { mapped = jsonAdapterFactory.getTypeAdapter( constructorConstructor, context, fieldType, annotation); } final boolean jsonAdapterPresent = mapped ! = null; if (mapped == null) mapped = context.getAdapter(fieldType); final TypeAdapter<? > typeAdapter = mapped; return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree @Override void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException { Object fieldValue = field.get(value); TypeAdapter t = jsonAdapterPresent ? typeAdapter : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType()); t.write(writer, fieldValue); } @Override void read(JsonReader reader, Object value) throws IOException, IllegalAccessException { //typeAdapter.read Object fieldValue = typeAdapter.read(reader); if (fieldValue ! = null || ! isPrimitive) { field.set(value, fieldValue); } } @Override public boolean writeField(Object value) throws IOException, IllegalAccessException { if (! serialized) return false; Object fieldValue = field.get(value); return fieldValue ! = value; // avoid recursion for example for Throwable.cause } }; }Copy the code

reference

  • Google gson/source
  • Do you really know how to use Gson? Gson Usage Guide