Gson用戶使用文檔

1.概述
2.Gson的目標
3.Gson的性能和擴展性
4.Gson的使用者
5.如何使用Gson

  • 通過Maven來使用Gson
  • 基本舉例
  • 對象舉例
  • 使用對象的好處
  • 嵌套類(包括內部類)
  • 數組舉例
  • 集合舉例
  • 集合的局限性
  • 泛型的序列化和反序列化
  • 任意類型集合的序列化和反序列化
  • 內置的序列化器和反序列化器
  • 定制的序列化和反序列化
  • 寫一個序列化解釋器
  • 寫一個反序列化解釋器
  • 寫一個實例化工廠(Writing a Instance Creator)
  • 一個參數化類型的實例工廠(InstanceCreator for a Parameterized Type)
  • 簡潔和美觀的Json格式化輸出
  • 支持Null對象
  • 支持版本化
  • 忽略序列化和反序列化中的字段
  • Java修飾符忽略
  • Gson's @Expose
  • 自定義忽略策略
  • JSON字段取名幫助
  • 通過經典的序列化器和反序列化器分享狀態

  • 6.在設計Gson中一些問題
    7.未來Gson需要加強的方面

概述


Gson是一個Java庫,它不僅可以把Java對象轉化為Json格式,它也能將一段Json格式的字符串轉化為相對于的Java對象。
Gson適用于所有Java對象,即使是那些你不知道源代碼的對象。
Gson的目標


  • 提供簡單易用的方法比如 toString() ,構造方法來轉化JAVA為JSON以及反轉化。
  • 提供已存在的不可修改對象轉化為JSON以及反轉化。
  • 提供對象的慣例表示
  • 支持任意復雜對象
  • 生成健壯 可讀的JSON輸出

Gson的性能和擴展性


這里提供我們跑測試的電腦配置(dual opteron, 8GB RAM, 64-bit Ubuntu),你可以通過使用PerformanceTest類來運行測試

  • 能正常反序列化25MB大小的字符串(參照 PerformanceTest中的disabled_testStringDeserializationPerformance)
  • 超大集合:
  • 能正常序列化一百四十萬對象(參見PerformanceTest中的disabled_testLargeCollectionSerialization)
  • 能正常反序列化87000個對象的集合(參見PerformanceTest中的disabled_testLargeCollectionDeserialization)
  • Gson 1.4 將反序列的字節數組和集合限制從80KB提高到11MB。
    注意:刪除 disabled_前綴 來運行這些TEST,我們使用這個前綴來阻止運行這些測試樣例當我們運行Junit的測試樣例。

Gson的使用者


Gson最初創建是為了Google內部大量的項目所使用,它現在被很多的開源項目和公司使用。

使用Gson


你可以使用 new Gson()方法 作為使用Gson的開始。 還有一個類GsonBuilder來創建一個Gson實例, 通過這個類你可以設置各種參數包括版本控制等等...

當你調用Json操作時,Gson實例不會維護任何狀態, 所以你可以所以復用相同的對象來重復Json的序列化和反序列操作。

通過Maven來使用Gson


通過Maven2/3來使用Gson, 添加下面的依賴來在Maven使用Gson
<dependencies> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.5</version> <scope>compile</scope> </dependency> </dependencies>
現在你的Maven項目就可以使用Gson

基礎舉例


// SerializationGson 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.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);

對象舉例


class BagOfPrimitives { private int value1 = 1; private String value2 = "abc"; private transient int value3 = 3; BagOfPrimitives() { // no-args constructor } } // Serialization BagOfPrimitives obj = new BagOfPrimitives(); Gson gson = new Gson(); String json = gson.toJson(obj); // ==> json is {"value1":1,"value2":"abc"}
注意 不要序列對象中使用循環引用,因為它將會導致無限遞歸
// DeserializationBagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);// ==> obj2 is just like obj

使用對象時的一些細節

  • 最好使用private修飾符
  • 不需要通過注釋來表明哪些字段需要被序列化和反序列化,默認當前類的所有字段都會被包括
  • 如果一個字段被表示為 transient(默認) 它將會被忽略 不會被包含在Json序列化或者反序列化中
  • nulls會被正確處理
  • 當序列化時 一個Null字段會在輸出時被跳過
  • 當反序列化時 Json中一個缺失的實體會自動設置相對應的字段為null
  • 如果一個字段是合成的,它會被直接忽略 也不會被包含在JSON的序列化和反序列化中。
  • 在內部類 匿名類 本地類中 相對應外部類的字段會被忽略,也不會被包含在JSON的序列化和反序列化中。

嵌套類(包括內部類)

Gson可以非常輕松的序列化靜態內部類
Gson也可以反序列化靜態類,Gson不能自動反序列化完全內部類(pure inner classes) 因為他們無參構造函數需要一個內部對象的引用,然而這個引用在反序列化的時候是不可用的,你可以這樣處理這個問題,要么使這個內部類靜態化,或者為他提供一個靜態工廠方法(instanceCreator),舉個例子:
public class A { public String a; class B { public String b; public B() { // No args constructor for B } } }
注意 上面的B類默認是不能被Gson序列化
Gson 不能反序列化{"a":"abc"} 進B實例 因為B類是一個內部類 如果它被定義為靜態類ClassB Gson就能反序列化字符串 另外一個解決方案就是為B類寫一個靜態工廠方法
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(); } }
上面解決方法是可行的,但是并不建議這么使用

數組舉例


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 ints
我們也支持多維數組,任何復雜元素類型。

集合舉例

Gson gson = new Gson(); Collection<Integer> ints = Lists.immutableList(1,2,3,4,5); // SerializationString json = gson.toJson(ints); // ==> json is [1,2,3,4,5] // Deserialization Type collectionType = new TypeToken<Collection<Integer>>(){}.getType(); Collection<Integer> ints2 = gson.fromJson(json, collectionType); // ==> ints2 is same as ints
十分可怕: 注意我們是如何定義集合的類型,但是不幸的是,在Java中沒有辦法獲取。

集合的局限性

  • 可以序列化集合的任意對象 但是無法從中反序列化
  • 因為使用者根本沒有辦法聲明一個結果對象的類型
  • 當序列化的時候,集合必須是一個特定泛型
    當遵循好的JAVA編碼格式, 這一切都能解釋通, 也不會成為問題。

序列化和反序列泛型類型

當你調用 toJson(obj) Gson會調用 obj.getClass來獲取序列化字段的信息。類似,你可以特意指定MyClass.class 對象在fromJson(json,MyClass.class)方法,這種方式運行正常當它不是泛型類型,然而,當對象是一個泛型類型的時候,泛型類型的信息會丟失因為Java類型的擦拭,下面這個例子來展示這一點。
class Foo<T> { T value; } Gson gson = new Gson(); Foo<Bar> foo = new Foo<Bar>(); gson.toJson(foo); // May not serialize foo.value correctly gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar
上面的代碼不能解析Bar類型的值 ,因為當Gson調用list.getclass()來獲得類的信息時候,這個方法返回一個未處理的類 Foo.class 這意味著Gson根本無法知道這是一個Foo<Bar>類型的對象,而不是一個無格式的Foo

你可以通過為你的泛型類型指定一個正確的參數來解決這個問題,你也可以通過使用TypeToken類來處理這個問題。

Type fooType = new TypeToken<Foo<Bar>>() {}.getType(); gson.toJson(foo, fooType); gson.fromJson(json, fooType);
我們通常獲取fooType 的方式是定義一個匿名內部類,其中包含一個getType方法返回一個全部的參數類型。

任意類型集合的序列化和反序列化


有時 你處理的是JSON數組包含了混合類型 例如 ['hello',5, {name:'GREETINGS',source:'guest'}]
集合實現這種JSON格式是通過:
Collection collection = new ArrayList(); collection.add("hello"); collection.add(5); collection.add(new Event("GREETINGS", "guest"));
然后event類是這樣定義的:
class Event { private String name; private String source; private Event(String name, String source) { this.name = name; this.source = source; } }
你不需要做額外的操作 當用Gson的toJson(collection)來序列化集合 獲得想要的輸出結果

然而 使用fromJson(json,Collection.class)反序列化卻失效, 因為Gson沒辦法知道如何將結果映射到類型上,Gson要求你提供一個泛型版本的集合類型在fromJson(), 所以你有三個選擇:

  1. 使用 Gson's 解析API(低版本的流解析或者DOM解析) 來解析數組元素 然后使用Gson.fromJson()在對應的數組元素上,這是一個比較好的解決方案,通過一個例子來說明如何使用。
  2. 為Collection.class注冊一個類型adapter,來把每一個數組成員都映射到對應的對象,這個方法不好之處在于它會混淆其他集合類型的反序列化
  3. 為MyCollectionMemberType注冊一個類型adapter 通過
    Collection<MyCollectionMemberType>來使用fromjson()
    這個方法僅用于數組以頂層元素出現 或者 你可以通過集合的類型Collection<MyCollectionMemberType>來改變字段的類型
    .

內置的序列化器和反序列化器

Gson擁有內置的序列化器和反序列化器,為那些默認顯示可能不合適的常用類,下面是這些類的清單:

  1. java.net.URL to match it with strings like "https://github.com/google/gson/"
  2. java.net.URI to match it with strings like "/google/gson/"
    你也可以找一些常用類的源代碼 比如JodaTime

定制的序列化和反序列化

有時 默認的表示可能不是你想要的,這常常發生當處理庫類的時候(DateTime, etc)Gson允許你來注冊你直接的常用的序列化和反序列化, 通常通過定義這兩部分來完成。

  • Json Serialiers: 需要為對象定義序列化
  • Json Deserializers: 為需要的類型定義反序列化
  • InstanceCreator: 一般不需要 如果無參構造器可以用或者反序列化以及被注冊

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());

寫一個序列化器


如下是為 JodaTime DateTime class寫一個定制的序列化器
private class DateTimeSerializer implements JsonSerializer<DateTime> { public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); } }
Gson調用serialize方法當有DateTime對象需要序列化

寫一個反序列化器

如下是為 JodaTime DateTime class寫一個定制的反序列化器
private class DateTimeDeserializer implements JsonDeserializer<DateTime> { public DateTime deserialize(JsonElement json, Type typeOfT,JsonDeserializationContext context) throws JsonParseException { return new DateTime(json.getAsJsonPrimitive().getAsString()); } }
Gson調用deserialize 當它需要反序列化JSON字符串碎片進DateTime對象

序列化器和反序列化器需要注意事項

通常你要注冊單個處理器來為所有反省類型對應一個未處理類型

  • 例如 假設你有一個ID類來表示或者轉化ID(比如 外部對應內部表示)
  • Id<T> 類型有相同的序列化為所有泛型類型
  • 反序列化非常相似但是不完全相同
  • 需要調用new Id(Class<T>,String)來返回Id<T>的實例

編寫靜態工廠方法

當反序列化一個對象時,Gson需要創建以個默認類的實例,一個好的類意味著序列化和反序列化都應該有一個無參構造器

  • public private都行
    通常 靜態工廠方法在你處理庫類時卻沒有定義一個無參構造器
    private class MoneyInstanceCreator implements InstanceCreator<Money> { public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); } }
    類型需要是對應的泛型類型
  • 當構造器需要制定的泛型類型信息非常有用
  • 舉例 如果Id類能存儲那些Id被創建的類

參數化類型的靜態工廠方法


有時候 你需要實例化的類型是參數化類型,一般,這不是一個問題 應為實際創建的實例是raw類型,如下是一個例子
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(); }}

然后,有時你確實需要創建一個基于現有的參數化類型,如果這樣的話,你可以把參數化類型傳給createInstance方法 像如下:
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); }}
在上面例子中, 一個ID類的實例是不能被創建的 如果沒有傳入參數化類型的真實類型。我們是通過傳入方法參數type來解決這個問題,類型對象在這個例子中是 Id<Foo>的Java參數化類型的表示,真正的實例應該被綁定到Id<Foo> 因為Id類僅有一個參數化類型的參數 T 我們使用由持有Foo.class的getActualTypeArgument()零元素的類型數組。

簡練 優雅打印JSON輸出格式


默認JSON輸出是由Gson提供的簡練JSON格式,這意味著JSON輸出格式是沒有任何空格,因此 在名 、值、對象數組、對象是沒有任何空格的。與此同時null字段也會在輸出時被忽略(注意: null值還是會存在集合/數組對象中) 在[Null Object Support] 中有更多關于如何配置Gson來輸出所有的Null值

如果你喜歡使用優雅的打印功能,你就一定通過使用GsonBuilder要配置你的Gson實例
JsonFormatter方法是不會通過我們的公有api暴露出去的,所以客戶端是無法通過配置默認的答應設置/間距來輸出格式化的JSON 現在,我們僅僅提供一個默認的JsonPrintFormatter方法 這個方法默認一場的長度是80字節 2字節縮進 4字節右邊距

下面通過一個例子展示如何配置Gson實例,使用默認的JsonPrintFormatter
而不是JsonCompactFormatter
Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(someObject);

Null對象支持


對于null對象 Gson的默認處理方式是直接忽略 這將提供更簡練的輸出格式,然而 客戶端必須提供一個默認值當JSON格式需要轉會對應的JAVA對象中是
你可以配置Gson實例來輸出null:
Gson gson = new GsonBuilder().serializeNulls().create();
例子如下:
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);
輸出結果如下:
{"s":null,"i":5} null

版本支持


可以使用@Since注解來維持相同的對象的不同版本,這個注釋可以被用來類、域、在未來的版本也會支持 Methods ,為了提高這個功能的影響力,你必須配置你的Gson實例忽略任何版本更好的域 對象。如果Gson實例沒有設置任何版本 那么序列化和反序列化所有字段和類 并無視版本。
public class VersionedClass { @Since(1.1) private final String newerField; @Since(1.0) private final String newField; private final String field; public VersionedClass() { this.newerField = "newer"; this.newField = "new"; this.field = "old"; }}VersionedClass versionedObject = new VersionedClass();Gson gson = new GsonBuilder().setVersion(1.0).create();String jsonOutput = gson.toJson(someObject);System.out.println(jsonOutput);System.out.println();gson = new Gson();jsonOutput = gson.toJson(someObject);System.out.println(jsonOutput);
輸出結果
`
{"newField":"new","field":"old"}

{"newerField":"newer","newField":"new","field":"old"}
`

忽略序列化和反序列化中的字段


Java Modifier Exclusion

默認 你可以表示一個字段為transient 它就會被忽略, 同樣一個字段被表示為static也會被忽略,如果你想加入一些transicent字段 你可以按下面來操作:
import java.lang.reflect.Modifier;Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC) .create();
注意 你可以使用任意多的修飾符常量給excludeFieldsWithModifiers方法 例如

Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) .create();

Gson's @Expose

如果上面的忽略字段和類的方法對你不起作用 你也可以寫自己的忽略策略并配置到Gson里面,參照 ExclusionStrategy
JavaDoc 查看更多的信息
下面的例子展示如果忽略字段被注釋為@Foo 并忽略頂層類String類。
@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);}
輸出結果:
{"longField":1234}

JSON字段命名支持

Gson支持預定義字段命名策略,包含標準JAVA字段命名(駱駝峰命名 這玩意不是微軟定義的嗎 以小寫字母開始--- sampleFieldNameInJava),并用它來給Json字段命名。參照 FieldNamingPolicy類來獲取更多關于預定義命名策略
它也提供一套基于注釋策略來允許客戶端自定義各自字段基礎,注意基于注釋策略有字段命名確定機制 ,如果注釋值提供的字段命名不合法則會包Runtime異常
下面是一個如何使用的例子:
`
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);

輸出結果如下:
{"custom_naming":"first","SomeOtherField":"second"}
`
如果你有一個自定義的命名策略的需要(見討論),你可以使用“serializedname注釋。

在自定義序列化和反序列化中共享狀態


有時你需要在自定義序列化/反序列化器共享狀態(見討論)。你可以使用以下三種策略來完成這個:
1.在靜態域存儲分享的狀態
2.聲明序列化器或者反序列化器作為父類型的內部類,并使用父類型的實例字段來存儲共享狀態
3.使用Java ThreadLocal
1.2不是線程安全,3是線程安全的


此外gson的對象模型和數據綁定,您可以使用gson讀取和寫入流。您還可以將流媒體和對象模型的訪問組合起來,以獲得最佳的兩種方法。

在設計gson時的一些問題

當我們設計Gson的一些討論 詳見Gson設計文檔。它還包括與其他Java庫,可用于JSON轉換gson比較。

Gson未來加強方向


詳情查看最新列表關于需要加強的方向,或者你想提一些新的建議,在項目網站上看到問題部分。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容