探索Android開(kāi)源框架 - 8. Gson使用及源碼解析

  • Gson 是 我們經(jīng)常用來(lái)在 Java 對(duì)象和 JSON 數(shù)據(jù)之間進(jìn)行映射的 庫(kù),今天我們就來(lái)分別講一講其使用和源碼分析

使用詳解

1. 基本的解析與生成

  • Gson提供了fromJson() 和toJson() 兩個(gè)直接用于解析和生成的方法候齿,前者實(shí)現(xiàn)反序列化,后者實(shí)現(xiàn)了序列化
  1. 解析基本數(shù)據(jù)類(lèi)型
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")
  1. 生成基本數(shù)據(jù)類(lèi)型
val gson = Gson()
val jsonI: String = gson.toJson(100) // 100
val jsonD: String = gson.toJson(99.99) // 100
val jsonB: String = gson.toJson(false) // false
val jsonS: String = gson.toJson("String") //"String"
log("jsonI=$jsonI,jsonD=$jsonD,jsonB=$jsonB,jsonS=$jsonS")
  1. 解析及生成自定義對(duì)象
//先定義一個(gè)數(shù)據(jù)模型類(lèi),這里需要注意的一點(diǎn)是在方法內(nèi)部定義的類(lèi)gson解析和生成都是null科贬,所以應(yīng)該放到類(lèi)或單獨(dú)的文件中定義
data class UserInfo(var name: String?, var age: Int?, var emailAddress: String? )
//創(chuàng)建自定義對(duì)象
val user = UserInfo(name = "222", age = 18, "xxx@163.com")
log("user=${user}")
//轉(zhuǎn)為json字符串
val gson = Gson()
val userStr = gson.toJson(user)
log("userStr:$userStr")
//從字符串轉(zhuǎn)為自定義對(duì)象
val user2: UserInfo? = gson.fromJson(userStr, UserInfo::class.java)
log("user2=${user2}")

2. 屬性重命名

@SerializedName 注解的使用
  • @SerializedName接收兩個(gè)參數(shù)惦蚊,value字符串對(duì)屬性重命名及alternate數(shù)組為屬性提供多個(gè)備選屬性名
  • toJson后的字符串中的屬性名會(huì)以value中的值為準(zhǔn)
  • 為字段提供備選屬性名汉规,當(dāng)多種情況同時(shí)出時(shí)干旧,以最后一個(gè)出現(xiàn)的值為準(zhǔn)
  1. 為數(shù)據(jù)模型類(lèi)添加注解
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,
)
  1. 解析與生成json字符串
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")
POJO與JSON的字段映射規(guī)則
  • GsonBuilder提供了setFieldNamingPolicy和setFieldNamingStrategy 兩個(gè)方法;
  1. setFieldNamingPolicy方法與FieldNamingPolicy枚舉配合使用斥杜,提供了如下幾種選擇
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, "xxx@163.com")
log("setFieldNamingPolicy:${gson.toJson(UserInfo3())}")
  1. setFieldNamingStrategy與Gson提供的FieldNamingStrategy接口配合使用 實(shí)現(xiàn)自己的規(guī)則
//1. 實(shí)現(xiàn)FieldNamingStrategy接口,定義自己的規(guī)則
class FieldStrategy {
    /**
     * 采用全部大寫(xiě)的形式
     */
    class AllUpperCaseStrategy : FieldNamingStrategy {
        override fun translateName(f: Field): String {
            return f.name.uppercase(Locale.getDefault())
        }
    }

    /**
     * 添加前置 和 后置 注解
     */
    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.使用
val gson = GsonBuilder()
    .setFieldNamingStrategy(FieldStrategy.AllUpperCaseStrategy())
    .setFieldNamingStrategy(FieldStrategy.GsonKeyValueCaseStrategy())
    .create()
val user = UserInfo(name = "222", age = 18, "xxx@163.com")
log("setFieldNamingStrategy:${gson.toJson(user)}")
  • @SerializedName注解擁有最高優(yōu)先級(jí)高蜂,在加有@SerializedName注解的字段上FieldNamingStrategy不生效

使用GsonBuilder

  • 上面我們沒(méi)有通過(guò)Gson()構(gòu)造方法來(lái)創(chuàng)建Gson實(shí)例聪黎,而是使用了GsonBuilder,下面介紹一些其方法
val gsonDIY = GsonBuilder()
    //序列化null,Gson在默認(rèn)情況下是不動(dòng)導(dǎo)出值null的鍵的
    .serializeNulls()
    // 設(shè)置日期時(shí)間格式备恤,另有2個(gè)重載方法
    // 在序列化和反序化時(shí)均生效
    .setDateFormat("yyyy-MM-dd")
    // 禁此序列化內(nèi)部類(lèi)
    .disableInnerClassSerialization()
    //生成不可執(zhí)行的Json, 其實(shí)就是多了 )]}' 這4個(gè)字符
    .generateNonExecutableJson()
    //禁止轉(zhuǎn)義html標(biāo)簽
    .disableHtmlEscaping()
    //格式化輸出
    .setPrettyPrinting()
    .create() //生成配置好的Gson

//使用
val user8 = UserInfo("888", 18)
val user8Str = gsonDIY.toJson(user8)
log("user8 toJson:${user8Str}")
log("fromJson user8Str:${gsonDIY.fromJson(user8Str, UserInfo::class.java)}")

泛型

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")
//Java泛型使用時(shí)要注意泛型擦除,Gson為我們提供了TypeToken來(lái)實(shí)現(xiàn)對(duì)泛型的支持
val userList2: List<UserInfo?>? =
    gson.fromJson(jsonArray, object : TypeToken<List<UserInfo?>?>() {}.type)
log("userList2=$userList2")
泛型的封裝
  • 但是每次解析泛型都要寫(xiě)TypeToken就很麻煩稿饰,所以我們可以在工具類(lèi)中做一些簡(jiǎn)單的封裝
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
}
  • 或者涉及到實(shí)際業(yè)務(wù),比如網(wǎng)絡(luò)數(shù)據(jù)的解析露泊,還可以像下面這樣再進(jìn)一步封裝一下
/**
 * 解析data是object的情況
 */
fun <T> fromJson2Object(json: String?, clazz: Class<T>): BaseHttpResponse<T>? {
    val type: Type = getType(BaseHttpResponse::class.java, clazz)
    return fromJson(json, type)
}

/**
 * 解析data是array的情況
 */
fun <T> fromJson2Array(json: String?, clazz: Class<T>): BaseHttpResponse<MutableList<T?>?>? {
    // 生成List<T> 中的 List<T>
    val listType: Type = getListType(clazz)
    // 根據(jù)List<T>生成完整的BaseHttpResponse<List<T>>
    val type: Type = getType(BaseHttpResponse::class.java, listType)
    return fromJson(json, type)
}

/**
 * 解析data是Map的情況
 */
fun <K, T> fromJson2Map(json: String?, clazz1: Class<K>, clazz2: Class<T>): BaseHttpResponse<MutableMap<K?, T?>?>? {
    // 生成BaseHttpResponse<Map<K,T>> 中的 Map<K,T>
    val mapType: Type = getMapType(clazz1, clazz2)
    // 根據(jù)Map<K,T>生成完整的BaseHttpResponse<Map<K,T>>
    val type: Type = getType(BaseHttpResponse::class.java, mapType)
    return fromJson(json, type)
}
  • 上面封裝的使用測(cè)試如下
//網(wǎng)絡(luò)數(shù)據(jù)的基類(lèi)
data class BaseHttpResponse<T>(
    @SerializedName(value = "code", alternate = ["Status", "status"]) var code: Int? = null,
    @SerializedName(value = "message", alternate = ["Msg", "msg"]) var message: String? = null,
    @SerializedName(value = "data", alternate = ["Data"]) var data: T? = null
)
//對(duì)BaseHttpResponse<UserInfo3>的解析
val baseHttpResponse1=BaseHttpResponse(111,"aaa",UserInfo3("ljy",18))
val strResponse1:String=GsonUtils.toJson(baseHttpResponse1)
log("fromJsonObject:${GsonUtils.fromJson2Object(strResponse1,UserInfo3::class.java)}")

//對(duì)List<UserInfo3>的解析
val list=mutableListOf(UserInfo3("ljy",18),UserInfo3("qwer",19))
log("fromJson(json,Type):${GsonUtils.fromJson<List<UserInfo3>>(GsonUtils.toJson(list),GsonUtils.getListType(UserInfo3::class.java))}")
//對(duì)BaseHttpResponse<List<UserInfo3>>的解析
val baseHttpResponse2=BaseHttpResponse<List<UserInfo3>>(111,"aaa",list)
val strResponse2:String=GsonUtils.toJson(baseHttpResponse2)
log("fromJsonArray:${GsonUtils.fromJson2Array(strResponse2,UserInfo3::class.java)}")

//對(duì)Map<String,UserInfo3>的解析
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))}")
//對(duì)BaseHttpResponse<Map<String,UserInfo3>>的解析
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)}")

Gson的序列化喉镰、反序列化

Gson的流式反序列化
  1. 手動(dòng)的方式: 使用stream包下的JsonReader類(lèi)來(lái)手動(dòng)實(shí)現(xiàn)反序列化,和Android中使用pull解析XML是比較類(lèi)似的
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")
  1. 自動(dòng)方式最終都是通過(guò)JsonReader來(lái)實(shí)現(xiàn)的惭笑,如果第一個(gè)參數(shù)是String類(lèi)型侣姆,那么Gson會(huì)創(chuàng)建一個(gè)StringReader轉(zhuǎn)換成流操作,源碼如下:
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;
}
Gson的流式序列化
  1. 手動(dòng)
val user7 = UserInfo("777", 17, "7777@qq.com")
// 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")
  1. 自動(dòng)
// PrintStream(System.out) 、StringBuilder沉噩、StringBuffer和*Writer都實(shí)現(xiàn)了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();
//}

字段過(guò)濾的幾種方法

1. @Expose注解
  • 基于@Expose注解:需要導(dǎo)出的字段上加上@Expose 注解,不導(dǎo)出的字段不加
data class UserInfo(
    @Expose //@Expose提供了兩個(gè)屬性屁擅,且默認(rèn)值都為true
    var name: String?,
    @Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效
    var age: Int?,
    @Expose(deserialize = true,serialize = false) //反序列化時(shí)生效
    var emailAddress: String? = null,
    @Expose(deserialize = false,serialize = true) //序列化時(shí)生效
    var date: Date? = Date(),
    @Expose(deserialize = false,serialize = false) // 和不寫(xiě)注解一樣
    var temp: String? = "沒(méi)啥用的temp"
)
  • 在使用Gson時(shí)也要用GsonBuilder調(diào)用excludeFieldsWithoutExposeAnnotation方法使其支持@Expose
val gson3 = GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create()
gson3.toJson(user8)
log("user8 toJson:${gson3.toJson(user8)}")
2. 基于版本
  • Gson在對(duì)基于版本的字段導(dǎo)出提供了兩個(gè)注解 @Since 和 @Until,都接收一個(gè)Double值
data class UserInfo2(
    var name: String?,
    @Since(3.0)
    var age: Int?,
    @Until(6.0)
    var emailAddress: String? = null,
)
  • 配合GsonBuilder.setVersion(Double)使用,當(dāng)前版本(GsonBuilder中設(shè)置的版本) 大于等于Since的值時(shí)該字段導(dǎo)出,小于Until的值時(shí)該該字段導(dǎo)出
val user9 = UserInfo2("999", 19, "9999@qq.com")
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).create().toJson(user9))
val user9Str = "{\"age\":19,\"emailAddress\":\"9999@qq.com\",\"name\":\"999\"}"
log("user9Str version 1.0:" + GsonBuilder().setVersion(1.0).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))
3. 基于訪(fǎng)問(wèn)修飾符
  • 基于public产弹、static 派歌、final弯囊、private、protected這些訪(fǎng)問(wèn)修飾符胶果,通過(guò)GsonBuilder().excludeFieldsWithModifiers()方法進(jìn)行控制
//用不同的修飾符修飾模型類(lèi)Test的字段
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";
}
//排除final,static,private修飾的字段
val gson4 = GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
    .create()
log(gson4.toJson(Test()))
4. 基于策略(自定義規(guī)則)
  • GsonBuilder的addSerializationExclusionStrategy , addDeserializationExclusionStrategy 兩個(gè)方法分別針對(duì)序列化和反序化
  • 兩個(gè)方法入?yún)魅隕xclusionStrategy接口的實(shí)現(xiàn)類(lèi)匾嘱,在其中實(shí)現(xiàn)自己的規(guī)則
val gson5 = GsonBuilder()
    .addSerializationExclusionStrategy(object : ExclusionStrategy {
        override fun shouldSkipField(f: FieldAttributes): Boolean {
            // 這里作判斷,決定要不要排除該字段,return true為排除
            if ("emailAddress" == f.name) return true //按字段名排除
            val expose = f.getAnnotation(Expose::class.java)
            return expose != null && !expose.deserialize //按注解排除:
        }

        override fun shouldSkipClass(clazz: Class<*>): Boolean {
            // 直接排除某個(gè)類(lèi) 早抠,return true為排除,例如排除Boolean.kt和Boolean.java
            return clazz == Boolean::class || clazz == Boolean::class.java
        }
    })
    .create()
log("UserInfo3:${gson5.toJson(UserInfo3())}")
log("Test:${gson5.toJson(Test())}")

TypeAdapter

  • 用于接管某種類(lèi)型的序列化和反序列化過(guò)程霎烙,包含兩個(gè)注要方法 write(JsonWriter,T) 和 read(JsonReader),其它的方法都是final方法并最終調(diào)用這兩個(gè)抽象方法。
  1. 自定義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
    }
}
  1. 為UserInfo3注冊(cè)UserInfoTypeAdapter
val gson8 = GsonBuilder()
    .registerTypeAdapter(UserInfo3::class.java, UserInfoTypeAdapter())
    .create()
log(gson8.toJson(UserInfo3()))
log(gson8.fromJson("{\"name\":\"ljy\",\"age\":20,\"email_address\":\"xx@qq.com\",\"email\":\"qqq@qq.com\"}", UserInfo3::class.java))

JsonSerializer與JsonDeserializer

  • 但是如果想只接管序列化或反序列化其中之一呢蕊连?只接管序列化的過(guò)程就用 JsonSerializer 悬垃,只接管反序列化的過(guò)程就用 JsonDeserializer
  • 例如接口返回的數(shù)據(jù),某個(gè)字段是int型甘苍,如果有些情況下給你返了個(gè)空字符串怎么辦
  1. 用TypeAdapter實(shí)現(xiàn)
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
        }
    }
}
  1. 用JsonSerializer與JsonDeserializer實(shí)現(xiàn)
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
        }
    }

}
  1. 使用測(cè)試
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))
  • registerTypeAdapter與registerTypeHierarchyAdapter的區(qū)別:
    • registerTypeAdapter:支持泛型,不支持繼承
    • registerTypeHierarchyAdapter:不支持泛型,支持繼承
  • TypeAdapter與 JsonSerializer尝蠕、JsonDeserializer對(duì)比,TypeAdapter效率高,內(nèi)存占用小,支持Stream API
實(shí)戰(zhàn)
  • 服務(wù)器返回的數(shù)據(jù)中data字段類(lèi)型不固定载庭,比如請(qǐng)求成功data是一個(gè)List,不成功的時(shí)候是String類(lèi)型看彼,這樣前端在使用泛型解析的時(shí)候,怎么去處理呢囚聚?
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("返回空List")
            Collections.EMPTY_LIST
        }
    }).create()
log(gson11.toJson(listOf(UserInfo3())))
val type = object : TypeToken<List<UserInfo3?>?>() {}.type
log(gson11.fromJson("[{\"age\":20,\"emailAddress\":\"xx@qq.com\",\"height\":180,\"man\":true,\"name\":\"ljy\"}]", type))
log(gson11.fromJson("{}", type))
log(gson11.fromJson("暫無(wú)數(shù)據(jù)", type))

TypeAdapterFactory

  • 用于創(chuàng)建TypeAdapter的工廠(chǎng)類(lèi)靖榕,通過(guò)對(duì)比Type,確定有沒(méi)有對(duì)應(yīng)的TypeAdapter顽铸,沒(méi)有就返回null茁计,與GsonBuilder.registerTypeAdapterFactory配合使用
  • 比如根據(jù)當(dāng)為null時(shí),根據(jù)泛型是String則返回“”,是List則返回空l(shuí)ist
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, gson)
        }
        return null
    }

    /**
     * null替換成空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替換為""
                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>
    }

    /**
     * null 替換成空字符串
     */
    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替換為""
                if (reader.peek() == JsonToken.NULL) {
                    reader.nextNull()
                    return ""
                }
                return reader.nextString()
            }

        } as TypeAdapter<T>
    }
}

//使用
val gson10 = GsonBuilder()
    .registerTypeAdapterFactory(GsonDefaultAdapterFactory())
    .create()

@JsonAdapter注解

  • 用在自定義模型類(lèi)上跋破,接收一個(gè)參數(shù)簸淀,且參數(shù)類(lèi)型必須是TypeAdapter,TypeAdapterFactory,JsonSerializer或JsonDeserializer其中之一
//  @JsonAdapter(NumJsonSerializer::class)
//  @JsonAdapter(NumJsonDeserializer::class)
//  @JsonAdapter(NumTypeAdapter::class)
@JsonAdapter(KotlinAdapterFactory2::class)
data class UserInfo5(
    var name: String = "ljy",
    private var email: String? = "xxx@qq.com",
    private val mobile: String? = "12315",
)
  • 使用時(shí)就不用再使用 GsonBuilder去注冊(cè)UserTypeAdapter了
log("UserInfo5:${Gson().toJson(UserInfo5())}")

源碼解析

  • 使用時(shí)我們是從fromJson(反序列化) and toJson(序列化)這兩個(gè)最基礎(chǔ)的方法開(kāi)始毒返,那么我們來(lái)點(diǎn)進(jìn)去看看他們是如何實(shí)現(xiàn)的

fromJson方法

  • fromJson有多個(gè)重載方法租幕,最終都會(huì)調(diào)用到fromJson(JsonReader reader, Type typeOfT)
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    //Class<T>轉(zhuǎn)為T(mén)ype
    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;
    }
    //將String包裝為StringReader
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
}

public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    //將Reader包裝為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
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      T object = typeAdapter.read(reader);
      return object;
    } catch //。拧簸。劲绪。省略很多異常處理
    } finally {
      reader.setLenient(oldLenient);
    }
}
  • 從上面代碼可以看到最終調(diào)用了getAdapter來(lái)根據(jù)typeToken獲取TypeAdapter,再通過(guò)TypeAdapter.read方法最終反序列化數(shù)據(jù),上面的使用詳解中我們有介紹了如何自定義TypeAdapter,所以對(duì)read方法也就不默生了盆赤;

getAdapter方法

  • 那么來(lái)看一下getAdapter方法是如何實(shí)現(xiàn)的
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    //先從緩存ConcurrentHashMap<TypeToken<?>, TypeAdapter<?>> typeTokenCache中取
    TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
    if (cached != null) {
      return (TypeAdapter<T>) cached;
    }
    //再?gòu)腡hreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls 中取
    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);

      //緩存map和ThreadLocal中都沒(méi)有贾富,則循環(huán)List<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();
      }
    }
}
  • 其中通過(guò)ThreadLocal緩存TypeAdapter對(duì)象,不同的線(xiàn)程使用緩存來(lái)解析的時(shí)候互不影響
  • 通過(guò)上面代碼可以看到最終是在List<TypeAdapterFactory> factories 集合中獲取到TypeAdapter的牺六,factories里面都有啥呢颤枪,我們來(lái)看看Gson構(gòu)造方法中是如何初始化的

Gson的構(gòu)造方法

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.add艘蹋。。票灰。//太多了
    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);
}
  • 可以看到factories中添加了很多預(yù)制的TypeAdapterFactory女阀,我們拿出其中的STRING_FACTORY看一下是如何實(shí)現(xiàn)的
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);
    }
};
  • 可以看到其調(diào)用了newFactory方法, 其實(shí)就是判斷一下typeToken.getRawType() == type,就直接返回了入?yún)⒌腡ypeAdapter STRING
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 + "]";
      }
    };
}
  • 再回到getAdapter方法屑迂,循環(huán)factories中調(diào)用了factory.create方法浸策,其實(shí)就是上面newFactory中的實(shí)現(xiàn)的TypeAdapterFactory的create方法
  • 需要注意的一點(diǎn)是Gson初始化factories時(shí)的順序,首先是各種常用的基礎(chǔ)類(lèi)的TypeAdapterFactory屈糊,最后幾行中添加JsonAdapterAnnotationTypeAdapterFactory對(duì)象在ReflectiveTypeAdapterFactory對(duì)象之前的榛,如果對(duì)實(shí)體類(lèi)使用了@JsonAdapter且指定的適配器存在那么就會(huì)返回@JsonAdapter里指定的適配器而不返回ReflectiveTypeAdapterFactory創(chuàng)建的,這樣我們就可以自己接管后面的解析過(guò)程了
JsonAdapterAnnotationTypeAdapterFactory
  • JsonAdapterAnnotationTypeAdapterFactory的create方法如下
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);
}
ReflectiveTypeAdapterFactory
  • 沒(méi)有使用@JsonAdapter注解的返回ReflectiveTypeAdapterFactory實(shí)例逻锐,我們看看它的create方法
@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));
}
  • create中主要是調(diào)用getBoundFields方法,將實(shí)體類(lèi)中需要解析的字段添加一個(gè)集合里夫晌,在反序列化時(shí)進(jìn)行賦值
 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) {
      //得到實(shí)體類(lèi)所有的字段
      Field[] fields = raw.getDeclaredFields();
      for (Field field : fields) {
        //字段是否參與反序列化或者序列化過(guò)程
        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;
}
  • 其中調(diào)用了excludeField來(lái)判斷字段是否參與反序列化或者序列化過(guò)程,實(shí)現(xiàn)如下
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(clazz)檢查class類(lèi)型是否符合序列化或者反序列化要求,里面用到的Since和Until注解
//excludeClassInStrategy(clazz, serialize)通過(guò)加入自己的策略來(lái)控制字段是否要參與解析
//excluder.excludeField(f, serialize)過(guò)濾字段
public boolean excludeClass(Class<?> clazz, boolean serialize) {
      return excludeClassChecks(clazz) ||
              excludeClassInStrategy(clazz, serialize);
}
  • 回到getBoundFields中昧诱,其調(diào)用了createBoundField來(lái)創(chuàng)建BoundField
createBoundField
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
      final Gson context, final Field field, final String name,
      final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
    //是否是基本數(shù)據(jù)類(lèi)型
    final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
    // special casing primitives here saves ~5% on Android...
    //@JsonAdapter注解晓淀,如果實(shí)體類(lèi)某屬性使用了@JsonAdapter,那么該屬性的序列化和反序列化將由指定的適配器接管盏档。
    //如果沒(méi)有這會(huì)從Gson初始化中查找對(duì)于的解析適配器
    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
      }
    };
}

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凶掰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蜈亩,更是在濱河造成了極大的恐慌懦窘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稚配,死亡現(xiàn)場(chǎng)離奇詭異畅涂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)道川,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)午衰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人冒萄,你說(shuō)我怎么就攤上這事臊岸。” “怎么了尊流?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵帅戒,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我崖技,道長(zhǎng)逻住,這世上最難降的妖魔是什么施流? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮鄙信,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忿晕。我一直安慰自己装诡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布践盼。 她就那樣靜靜地躺著鸦采,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咕幻。 梳的紋絲不亂的頭發(fā)上渔伯,一...
    開(kāi)封第一講書(shū)人閱讀 50,043評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音肄程,去河邊找鬼锣吼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蓝厌,可吹牛的內(nèi)容都是我干的玄叠。 我是一名探鬼主播,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拓提,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼读恃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起代态,我...
    開(kāi)封第一講書(shū)人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寺惫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蹦疑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體西雀,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年必尼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒋搜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡判莉,死狀恐怖豆挽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情券盅,我是刑警寧澤帮哈,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站锰镀,受9級(jí)特大地震影響娘侍,放射性物質(zhì)發(fā)生泄漏咖刃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一憾筏、第九天 我趴在偏房一處隱蔽的房頂上張望嚎杨。 院中可真熱鬧,春花似錦氧腰、人聲如沸枫浙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)箩帚。三九已至,卻和暖如春黄痪,著一層夾襖步出監(jiān)牢的瞬間紧帕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工桅打, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留是嗜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓油额,卻偏偏與公主長(zhǎng)得像叠纷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子潦嘶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容