Swift&JSON 從入門到精通

在iOS中最常見的工作是將數(shù)據(jù)保存起來并通過網(wǎng)絡(luò)傳輸。但是在這之前,你需要將數(shù)據(jù)通過編碼序列化轉(zhuǎn)換成合適的格式醋粟。

圖片

同樣的,在你使用這些數(shù)據(jù)之前重归,你也需要將其轉(zhuǎn)換成合適的格式米愿。這個相反的過程被稱為解碼反序列化

圖片

在這個教程中鼻吮,你將學(xué)習(xí)到所有使用Swift進行編解碼所需要的知識育苟。包括這些:

  1. 蛇形命名駝峰命名格式之間轉(zhuǎn)換
  2. 自定義Coding keys
  3. 使用keyed,unkeyednested容器
  4. 處理嵌套類型,日期類型以及子類

這確實有點多,是時候開始動手了椎木!

開始動手

鏈接:提取碼:15B7

下載完成后宙搬,starter是該教程使用的版本笨腥。final是最終完成的版本。

我們打開本節(jié)代碼Nested types勇垛。使ToyEmployee遵循Codable協(xié)議:

struct Toy: Codable {
  ...
}
struct Employee: Codable {
  ...
}

Codable本身并不是一個協(xié)議,它只是另外兩個協(xié)議的別名:EncodableDecodable士鸥。你也行已經(jīng)猜到了闲孤,這兩個協(xié)議就是代表那些可以被編解碼的類型。

你無需再做其他事情烤礁,因為ToyEmployee的所有存儲屬性都是Codable的讼积。Swift標準庫中大多數(shù)類型(例如StringURL)都是支持Codable的脚仔。

添加一個JSONEncoderJSONDecoder來處理toysemployees的編解碼:

let encoder = JSONEncoder()
let decoder = JSONDecoder()

操作JSON我們只需做這些勤众!下面進入第一個挑戰(zhàn)!

編解碼嵌套類型

Employee包含了一個Toy屬性(這是個嵌套類型)鲤脏。編碼后的JSON結(jié)構(gòu)和Employee結(jié)構(gòu)體保持一致:

{
  "name" : "John Appleseed",
  "id" : 7,
  "favoriteToy" : {
    "name" : "Teddy Bear"
  }
}
public struct Employee: Codable {
  var name: String
  var id: Int
  var favoriteToy: Toy
}

JSON數(shù)據(jù)將name嵌套在favoriteToy之中们颜,并且所有的JSON字段名與ToyEmployee的存儲屬性名相同,所以基于結(jié)構(gòu)體的類型體系猎醇,JSON的結(jié)構(gòu)很容易理解窥突。

如果屬性名稱和JSON的字段名都相同,并且屬性都是Codable的硫嘶,那么我們可以很容易的將JSON轉(zhuǎn)換為數(shù)據(jù)模型阻问,或者反過來。現(xiàn)在來試一試:

// 1
let data = try encoder.encode(employee)
// 2
let string = String(data: data, encoding: .utf8)!

這里做了2件事:

  1. employee使用encode(_:)編碼成JSON沦疾。是不是很簡單称近!
  2. 從上一步的data中創(chuàng)建String,一遍可以查看其內(nèi)容哮塞。

這里的編碼過程會產(chǎn)生合法的數(shù)據(jù)刨秆,所以我們可以使用它重新創(chuàng)建employee

let sameEmployee = try decoder.decode(Employee.self, from: data)

好了,可以開始下一個挑戰(zhàn)了彻桃!

蛇形命名駝峰命名格式之間轉(zhuǎn)換

現(xiàn)在坛善,假設(shè)JSON的鍵名從駝峰格式(這樣looksLikeThis)轉(zhuǎn)換成了蛇形格式(這樣looks_like_this_instead)。但是邻眷,ToyEmployee的存儲屬性只能使用駝峰格式眠屎。幸運的是Foundation考慮到了這種情況。

打開本節(jié)代碼Snake case vs camel case肆饶,在編解碼器創(chuàng)建之后使用之前的位置添加下面的代碼:

encoder.keyEncodingStrategy = .convertToSnakeCase
decoder.keyDecodingStrategy = .convertFromSnakeCase

運行代碼改衩,檢查snakeString,編碼后的employee產(chǎn)生下面的內(nèi)容:

{
  "name" : "John Appleseed",
  "id" : 7,
  "favorite_toy" : {
    "name" : "Teddy Bear"
  }
}
圖片

自定義Coding keys

現(xiàn)在驯镊,假設(shè)JOSN的格式再一次改變葫督,其使用的字段名和ToyEmployee中存儲屬性名不一致了:

{
  "name" : "John Appleseed",
  "id" : 7,
  "gift" : {
    "name" : "Teddy Bear"
  }
}

可以看到竭鞍,這里使用gift代替了原來的favoriteToy。這種情況我們需要自定義Coding keys橄镜。在我們的類型中添加一個特殊的枚舉類型偎快。打開本節(jié)代碼Custom coding keys,在Employee中添加下面的代碼:

enum CodingKeys: String, CodingKey {
  case name, id, favoriteToy = "gift"
}

這個特殊的枚舉遵循了CodingKey協(xié)議洽胶,并使用String類型的原始值晒夹。在這里我們可以讓favoriteToygift一一對應(yīng)起來。

在編解碼過程中姊氓,只會操作出現(xiàn)在枚舉中的cases丐怯,所以即使那些不需要指定一一對應(yīng)的屬性,也需要在枚舉中包含翔横,就像這里的nameid读跷。

運行playground,然后查看string的值禾唁,你會發(fā)現(xiàn)JSON字段名不在依賴存儲屬性名稱效览,這得益于自定義的Coding keys

繼續(xù)下一個挑戰(zhàn)蟀俊!

處理扁平化的JSON

現(xiàn)在钦铺,JSON的格式變成下面這樣:

{
  "name" : "John Appleseed",
  "id" : 7,
  "gift" : "Teddy Bear"
}

這里不在有嵌套結(jié)構(gòu),和我們的模型結(jié)構(gòu)不一致了肢预。這種情況我們需要自定義編解碼過程矛洞。

打開本節(jié)代碼Keyed containers。這里有個Employee類型烫映,它遵循了Encodable沼本。同時我們使用extension讓它遵循了Decodable

這樣做的好處是锭沟,可以保留結(jié)構(gòu)體的逐一成員構(gòu)造器抽兆。如果我們在定義Employee時讓它遵循Decodable,它將失去這個構(gòu)造器族淮。添加下面的代碼到Employee中:

// 1
enum CodingKeys: CodingKey {
  case name, id, gift
}

func encode(to encoder: Encoder) throws {
  // 2
  var container = encoder.container(keyedBy: CodingKeys.self)
  // 3  
  try container.encode(name, forKey: .name)
  try container.encode(id, forKey: .id)
  // 4
  try container.encode(favoriteToy.name, forKey: .gift)
}

在之前簡單(指屬性名和鍵名一一對應(yīng)且嵌套層級相同)的示例中辫红,encode(to:)方法由編譯器自動實現(xiàn)了。現(xiàn)在我們需要手動實現(xiàn)祝辣。

  1. 創(chuàng)建CodingKeys表示JSON的字段贴妻。因為我們沒有做任何的關(guān)系映射,所以不必聲明它的原始類型為String蝙斜。
  2. encoder中獲取KeyedEncodingContainer容器名惩。這就像一個字典,我們可以存儲屬性的值到其中孕荠,這樣就進行了編碼娩鹉。
  3. 編碼nameid屬性到容器中攻谁。
  4. 使用gift鍵,直接將toy的名字編碼到容器中弯予。

運行playground戚宦,然后查看string的值,你會發(fā)現(xiàn)它符合上面JSON的格式熙涤。我們可以選擇使用什么字段名編碼一個屬性值阁苞,這給了我們很大的靈活性。

和編碼過程類似祠挫,簡單版本的init(from:)方法可以由編譯器自動實現(xiàn)。但是這里我們需要手動實現(xiàn)悼沿,使用下面的代碼替換fatalError("To do")

// 1
let container = try decoder.container(keyedBy: CodingKeys.self)
// 2
name = try container.decode(String.self, forKey: .name)
id = try container.decode(Int.self, forKey: .id)
// 3
let gift = try container.decode(String.self, forKey: .gift)
favoriteToy = Toy(name: gift)

然后添加下面的代碼等舔,就可以從JSON中重新創(chuàng)建employee

let sameEmployee = try decoder.decode(Employee.self, from: data)

處理多級嵌套的JSON

現(xiàn)在,JSON的格式變成下面這樣:

{
  "name" : "John Appleseed",
  "id" : 7,
  "gift" : {
    "toy" : {
      "name" : "Teddy Bear"
    }
  }
}

name字段在toy字段中糟趾,而toy又在gift字段中慌植。如何解析成我們定義的數(shù)據(jù)模型呢?

打開本節(jié)代碼Nested keyed containers义郑,添加下面的代碼到Employee

// 1  
enum CodingKeys: CodingKey {  
  case name, id, gift
}
// 2
enum GiftKeys: CodingKey {
  case toy
}
// 3
func encode(to encoder: Encoder) throws {
  var container = encoder.container(keyedBy: CodingKeys.self)
  try container.encode(name, forKey: .name)
  try container.encode(id, forKey: .id)
  // 4  
  var giftContainer = container
    .nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
  try giftContainer.encode(favoriteToy, forKey: .toy)
}

這里做了幾件事:

  1. 創(chuàng)建頂層的CodingKeys
  2. 創(chuàng)建用于解析gift字段的CodingKeys蝶柿,后續(xù)使用它創(chuàng)建容器
  3. 使用頂層容器編碼nameid
  4. 使用nestedContainer(keyedBy:forKey:)方法獲取用于編碼gift字段的容器,并將favoriteToy編碼進去

運行并查看string的值非驮,你會發(fā)現(xiàn)JSON的格式符合預(yù)期交汤。

解碼過程也很類似。添加下面的代碼:

extension Employee: Decodable {
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    id = try container.decode(Int.self, forKey: .id)
    let giftContainer = try container
      .nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
    favoriteToy = try giftContainer.decode(Toy.self, forKey: .toy)
  }
}

let sameEmployee = try decoder.decode(Employee.self, from: nestedData)

好了劫笙,我們已經(jīng)搞定了嵌套類型的容器芙扎。并從其中解碼出了sameEmployee

處理日期類型

現(xiàn)在填大,JSON里添加了日期字段戒洼,就像下面這樣:

{
  "id" : 7,
  "name" : "John Appleseed",
  "birthday" : "29-05-2019",
  "toy" : {
    "name" : "Teddy Bear"
  }
}

JSON中并沒有標準的日期格式。在JSONEncoderJSONDecoder使用日期類的timeIntervalSinceReferenceDate方法去處理(Date(timeIntervalSinceReferenceDate: interval))允华。

這里我們需要指定日期轉(zhuǎn)換策略圈浇。打開本節(jié)代碼Dates,在try encoder.encode(employee)之前添加下面的代碼:

// 1
extension DateFormatter {
  static let dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "dd-MM-yyyy"
    return formatter
  }()
}
// 2
encoder.dateEncodingStrategy = .formatted(.dateFormatter)
decoder.dateDecodingStrategy = .formatted(.dateFormatter)

這里主要做了2件事:

  1. DateFormatter的擴展中添加了格式化器靴寂,它的格式化形式滿足JSON中日期的格式磷蜀,并且是可以重用的。
  2. 設(shè)置dateEncodingStrategydateDecodingStrategy.formatted(.dateFormatter)榨汤,這樣編解碼時就會使用它去處理日期

運行并檢查dateString的內(nèi)容蠕搜,你會發(fā)現(xiàn)它符合預(yù)期。

處理子類

現(xiàn)在收壕,JSON格式變成了下面這樣:

{
  "toy" : {
    "name" : "Teddy Bear"
  },
  "employee" : {
    "name" : "John Appleseed",
    "id" : 7
  },
  "birthday" : 580794178.33482599
}

這里將Employee所需信息分開了妓灌。我們打算使用BasicEmployee去解析employee轨蛤。打開本節(jié)代碼Subclasses,使BasicEmployee遵循Codable

class BasicEmployee: Codable {

不出意外虫埂,編譯器報錯了祥山,因為GiftEmployee并沒有遵循Codable。我們繼續(xù)添加下面的代碼掉伏,就可以修正錯誤了:

// 1              
enum CodingKeys: CodingKey {
  case employee, birthday, toy
}  
// 2
required init(from decoder: Decoder) throws {
  let container = try decoder.container(keyedBy: CodingKeys.self)
  birthday = try container.decode(Date.self, forKey: .birthday)
  toy = try container.decode(Toy.self, forKey: .toy)
  // 3
  let baseDecoder = try container.superDecoder(forKey: .employee)
  try super.init(from: baseDecoder)
}

這里做了3件事:

  1. GiftEmployee中添加了CodingKeys缝呕。和JSON中的字段名對應(yīng)。
  2. decoder解碼出子類的屬性值斧散。
  3. 創(chuàng)建用于解碼父類屬性的Decoder供常,然后調(diào)用父類的方法初始化父類屬性。

下面我們繼續(xù)完成GiftEmployee的編碼方法:

override func encode(to encoder: Encoder) throws {
  var container = encoder.container(keyedBy: CodingKeys.self)
  try container.encode(birthday, forKey: .birthday)
  try container.encode(toy, forKey: .toy)
  let baseEncoder = container.superEncoder(forKey: .employee)
  try super.encode(to: baseEncoder)
}

和解碼過程類似鸡捐,我們先編碼了子類的屬性栈暇,然后獲取用于編碼父類的encoder。下面測試下結(jié)果:

let giftEmployee = GiftEmployee(name: "John Appleseed", id: 7, birthday: Date(),  toy: toy)
let giftData = try encoder.encode(giftEmployee)
let giftString = String(data: giftData, encoding: .utf8)!
let sameGiftEmployee = try decoder.decode(GiftEmployee.self, from: giftData)

運行并檢查giftString箍镜,你會發(fā)現(xiàn)其內(nèi)容符合預(yù)期源祈。學(xué)習(xí)了本節(jié),你就可以處理更復(fù)雜的繼承數(shù)據(jù)模型了色迂。

處理混合類型的數(shù)組

現(xiàn)在香缺,JSON格式變成了下面這樣:

[
  {
    "name" : "John Appleseed",
    "id" : 7
  },
  {
    "id" : 7,
    "name" : "John Appleseed",
    "birthday" : 580797832.94787002,
    "toy" : {
      "name" : "Teddy Bear"
    }
  }
]

這是個JSON數(shù)組,但是其內(nèi)部元素格式并不一致歇僧。打開本節(jié)代碼Polymorphic types图张,可以看到這里使用枚舉定義了不同類型的數(shù)據(jù)。

首先馏慨,我們讓AnyEmployee遵循Encodable協(xié)議:

enum AnyEmployee: Encodable { ... }

繼續(xù)在AnyEmployee中添加下面的代碼:

// 1
enum CodingKeys: CodingKey {
  case name, id, birthday, toy
}  
// 2
func encode(to encoder: Encoder) throws {
  var container = encoder.container(keyedBy: CodingKeys.self)

  switch self {
    case .defaultEmployee(let name, let id):
      try container.encode(name, forKey: .name)
      try container.encode(id, forKey: .id)
    case .customEmployee(let name, let id, let birthday, let toy):  
      try container.encode(name, forKey: .name)
      try container.encode(id, forKey: .id)
      try container.encode(birthday, forKey: .birthday)
      try container.encode(toy, forKey: .toy)
    case .noEmployee:
      let context = EncodingError.Context(codingPath: encoder.codingPath, 
                                          debugDescription: "Invalid employee!")
      throw EncodingError.invalidValue(self, context)
  }
}

這里我們主要做了兩件事:

  1. 定義了所有可能的鍵埂淮。
  2. 根據(jù)不同類型,對數(shù)據(jù)進行編碼写隶。

在代碼的最后添加下面的內(nèi)容來進行測試:

let employees = [AnyEmployee.defaultEmployee("John Appleseed", 7), 
                 AnyEmployee.customEmployee("John Appleseed", 7, Date(),toy)]
let employeesData = try encoder.encode(employees)
let employeesString = String(data: employeesData, encoding: .utf8)!

接下來的編碼過程有點復(fù)雜倔撞。繼續(xù)添加下面的代碼:

extension AnyEmployee: Decodable {
  init(from decoder: Decoder) throws {
    // 1
    let container = try decoder.container(keyedBy: CodingKeys.self) 
    let containerKeys = Set(container.allKeys)
    let defaultKeys = Set<CodingKeys>([.name, .id])
    let customKeys = Set<CodingKeys>([.name, .id, .birthday, .toy])

    // 2
   switch containerKeys {
      case defaultKeys:
        let name = try container.decode(String.self, forKey: .name)
        let id = try container.decode(Int.self, forKey: .id)
        self = .defaultEmployee(name, id)
      case customKeys:
        let name = try container.decode(String.self, forKey: .name)
        let id = try container.decode(Int.self, forKey: .id)
        let birthday = try container.decode(Date.self, forKey: .birthday)
        let toy = try container.decode(Toy.self, forKey: .toy)
        self = .customEmployee(name, id, birthday, toy)
      default:
        self = .noEmployee
    }
  }
}
// 3
let sameEmployees = try decoder.decode([AnyEmployee].self, from: employeesData)

解釋下上面的代碼:

  1. 獲取KeydContainer,并獲取其所有鍵慕趴。
  2. 根據(jù)不同的鍵痪蝇,實行不同的解析策略
  3. employeesData中解碼出[AnyEmployee]

個人感覺若數(shù)組中的元素可以用同一模型來表示,只是字段可能為空時冕房,直接將模型字段設(shè)為可選躏啰。當然這里也提供了解析不同模型的思路。

處理數(shù)組

現(xiàn)在耙册,我們有如下格式JSON:

[
  "teddy bear",
  "TEDDY BEAR",
  "Teddy Bear"
]

這里是一個數(shù)組给僵,并且其大小寫各不相同。此時我們不需要任何CodingKey,只需使用unkeyed container帝际。

打開本節(jié)代碼Unkeyed containers蔓同,添加下面的代碼到Label結(jié)構(gòu)體中:

func encode(to encoder: Encoder) throws {
  var container = encoder.unkeyedContainer()
  try container.encode(toy.name.lowercased())
  try container.encode(toy.name.uppercased())
  try container.encode(toy.name)
}

UnkeyedEncodingContainer和之前用到的KeyedEncodingContainer相似,但是它不需要CodingKey蹲诀,因為它將編碼數(shù)據(jù)寫入JSON數(shù)組中斑粱。這里我們編碼了3中不同的字符串到其中。

繼續(xù)解碼:

extension Label: Decodable {
  // 1
  init(from decoder: Decoder) throws {
    var container = try decoder.unkeyedContainer()
    var name = ""
    while !container.isAtEnd {
      name = try container.decode(String.self)
    }
    toy = Toy(name: name)
  }
}
let sameLabel = try decoder.decode(Label.self, from: labelData)

這里主要是獲取decoder.unkeyedContainer脯爪,獲取容器中最后一個值來初始化name则北。

處理嵌套在對象中的數(shù)組

現(xiàn)在我們有如下格式JSON:

{
  "name" : "Teddy Bear",
  "label" : [
    "teddy bear",
    "TEDDY BEAR",
    "Teddy Bear"
  ]
}

這次,標簽對應(yīng)在了label字段下痕慢。我們需要使用nested unkeyed containers去進行編解碼尚揣。

打開本節(jié)代碼Nested unkeyed containers,在Toy中添加下面的代碼:

func encode(to encoder: Encoder) throws {
  var container = encoder.container(keyedBy: CodingKeys.self)
  try container.encode(name, forKey: .name)
  var labelContainer = container.nestedUnkeyedContainer(forKey: .label)                   
  try labelContainer.encode(name.lowercased())
  try labelContainer.encode(name.uppercased())
  try labelContainer.encode(name)
}

這里我們創(chuàng)建了一個nested unkeyed container掖举,并填充了3個字符串惑艇。運行代碼,并查看string的值拇泛,可以看到預(yù)期結(jié)果。

繼續(xù)添加下面的代碼進行解碼:

extension Toy: Decodable {
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    var labelContainer = try container.nestedUnkeyedContainer(forKey: .label)
    var labelName = ""
    while !labelContainer.isAtEnd {
      labelName = try labelContainer.decode(String.self)
    }
    label = labelName
  }
}
let sameToy = try decoder.decode(Toy.self, from: data)

這里思灌,我們像之前一樣俺叭,使用unkeyed container的最后一個值初始化label字段,只不過獲取的是嵌套的容器泰偿。

處理可選字段

最后熄守,我們的模型中的屬性也可以是可選類型,container也提供了對應(yīng)的編解碼方法:

encodeIfPresent(value, forKey: key)
decodeIfPresent(type, forKey: key)

總結(jié)

今天我們由淺入深的學(xué)習(xí)了如何在Swift中處理JSON耗跛。其中自定義Coding keys裕照、處理子類等部分需要重點理解。希望對大家有所幫助调塌。

原文鏈接:
https://www.raywenderlich.com/3418439-encoding-and-decoding-in-swift

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晋南,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子羔砾,更是在濱河造成了極大的恐慌负间,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姜凄,死亡現(xiàn)場離奇詭異政溃,居然都是意外死亡,警方通過查閱死者的電腦和手機态秧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門董虱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人申鱼,你說我怎么就攤上這事愤诱≡仆罚” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵转锈,是天一觀的道長盘寡。 經(jīng)常有香客問我,道長撮慨,這世上最難降的妖魔是什么竿痰? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮砌溺,結(jié)果婚禮上影涉,老公的妹妹穿的比我還像新娘。我一直安慰自己规伐,他們只是感情好蟹倾,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猖闪,像睡著了一般鲜棠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上培慌,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天豁陆,我揣著相機與錄音,去河邊找鬼吵护。 笑死盒音,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的馅而。 我是一名探鬼主播祥诽,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓮恭!你這毒婦竟也來了雄坪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤偎血,失蹤者是張志新(化名)和其女友劉穎诸衔,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颇玷,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡笨农,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帖渠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谒亦。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出份招,到底是詐尸還是另有隱情切揭,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布锁摔,位于F島的核電站廓旬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谐腰。R本人自食惡果不足惜孕豹,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望十气。 院中可真熱鬧励背,春花似錦、人聲如沸砸西。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芹枷。三九已至衅疙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸳慈,已是汗流浹背炼蛤。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝶涩,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓絮识,卻偏偏與公主長得像绿聘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子次舌,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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