使用泛型與函數(shù)式思想高效解析 JSON

更多優(yōu)秀譯文請(qǐng)關(guān)注我們的微信公眾號(hào):learnSwift

原文連接:Efficient JSON in Swift with Functional Concepts and Generics

就在幾個(gè)月前,蘋果推出了一門全新的編程語(yǔ)言,其名為Swift, 這讓我們對(duì)未來(lái) iOS 和 OS X 開(kāi)發(fā)充滿了期待與興奮。人們紛紛開(kāi)始使用 Xcode Beta1 版本來(lái)進(jìn)行 Swift 開(kāi)發(fā),但是很快就發(fā)現(xiàn)解析 JSON 這一常見(jiàn)的操作在 Swift 中并不如在 Objectitve-C 中那樣快捷和方便。Swift 是一門靜態(tài)類型的語(yǔ)言,這意味我們不能簡(jiǎn)單地將對(duì)象賦值給一個(gè)特定類型的變量,并且讓編譯器相信這些對(duì)象就是我們所聲明的那種類型。在 Swift 當(dāng)中,編譯器會(huì)進(jìn)行檢查,以確保我們不會(huì)意外地觸發(fā)運(yùn)行時(shí)錯(cuò)誤。這使得我們可以依賴編譯器來(lái)寫出一些無(wú) bug 的代碼,同時(shí)我們必須做許多額外的工作來(lái)使編譯器不報(bào)錯(cuò)。在這篇文章當(dāng)中,我將使用函數(shù)式思想和泛型來(lái)探討如何編寫易讀高效的 JSON 解析代碼。

請(qǐng)求用戶(User)模型

我們要做的事就是將網(wǎng)絡(luò)請(qǐng)求獲得的數(shù)據(jù)解析成 JSON。之前我們一直使用的是 NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)方法,這個(gè)方法返回一個(gè)可選的 JSON 數(shù)據(jù)類型,如果解析過(guò)程出錯(cuò)會(huì)得到 NSError 類型的數(shù)據(jù)。在 Objective-C 當(dāng)中,JSON 的數(shù)據(jù)類型是一個(gè)可以包含任何其它數(shù)據(jù)類型的 NSDictionary類型。 而在 Swift 當(dāng)中, 新的字典類型要求我們必須顯式指定它所包含的數(shù)據(jù)的類型。JSON 數(shù)據(jù)被指定為Dictionary<String, AnyObject>類型。這里使用 AnyObject的原因是 JSON 的值有可能為 String、Double、 Bool、 ArrayDictionary 或者 null。當(dāng)我們使用 JSON 來(lái)生成模型數(shù)據(jù)時(shí),必須對(duì)每一個(gè)從 JSON 字典中獲取到的值進(jìn)行判斷,以確保這個(gè)值與我們模型中屬性的類型一致。

下面我們來(lái)看一個(gè)用戶(user)的模型:

struct User {
  let id: Int
  let name: String
  let email: String
}

然后,來(lái)看一下對(duì)當(dāng)前用戶的請(qǐng)求和響應(yīng)代碼:

func getUser(request: NSURLRequest, callback: (User) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
  { data, urlResponse, error in
    var jsonErrorOptional: NSError?
    let jsonOptional: AnyObject! = 
    NSJSONSerialization.JSONObjectWithData(data, 
    options: NSJSONReadingOptions(0), error: &jsonErrorOptional)
    if let json = jsonOptional as? Dictionary<String, AnyObject> {
      if let id = json["id"] as AnyObject? as? Int { 
      // 在 beta5 中,存在一個(gè) bug,所以我們首先要強(qiáng)行轉(zhuǎn)換成 AnyObject?
        if let name = json["name"] as AnyObject? as? String {
          if let email = json["email"] as AnyObject? as? String {
            let user = User(id: id, name: name, email: email)
            callback(user)
          }
        }
      }
    }
  }
  task.resume()
}

在一長(zhǎng)串的if-let語(yǔ)句之后,我們終于拿到User對(duì)象??梢韵胂笠幌拢绻粋€(gè)模型的屬性很多,這些代碼會(huì)有多丑。并且,這里我們沒(méi)有進(jìn)行錯(cuò)誤處理,這意味著,只要其中一步出錯(cuò)我們就獲取不到任何數(shù)據(jù)。最后并且最重要的一點(diǎn)是,我們必須對(duì)每個(gè)需要從網(wǎng)絡(luò) API 中獲取的模型寫一遍類似上面這樣的代碼,這將會(huì)導(dǎo)致很多重復(fù)代碼。

在對(duì)代碼進(jìn)行重構(gòu)之前,讓我們先對(duì)JSON的幾種類型定義別名,以使之后的代碼看起來(lái)更簡(jiǎn)潔。

typealias JSON = AnyObject
typealias JSONDictionary = Dictionary<String, JSON>
typealias JSONArray = Array<JSON>

重構(gòu):添加錯(cuò)誤處理

首先,我們將通過(guò)學(xué)習(xí)第一個(gè)函數(shù)式編程的概念,Either<A, B>類型,來(lái)對(duì)代碼進(jìn)行重構(gòu),以使其能進(jìn)行錯(cuò)誤處理。這可以使代碼在正確的情況下返回用戶對(duì)象,而在出錯(cuò)時(shí)返回一個(gè)錯(cuò)誤對(duì)象。在 Swift 當(dāng)中可以使用如下方法來(lái)實(shí)現(xiàn) Either<A, B>

enum Either<A, B> {
  case Left(A)
  case Right(B)
}

我們可以使用 Either<NSError, User> 作為傳入回調(diào)的參數(shù),這樣調(diào)用者便可以直接處理解析過(guò)的User對(duì)象或者錯(cuò)誤。

func getUser(request: NSURLRequest, callback: 
                (Either<NSError, User>) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request) 
  { data, urlResponse, error in
    // 如果響應(yīng)返回錯(cuò)誤,我們將把錯(cuò)誤發(fā)送給回調(diào)
    if let err = error {
      callback(.Left(err))
      return
    }
    
    var jsonErrorOptional: NSError?
    let jsonOptional: JSON! = 
    NSJSONSerialization.JSONObjectWithData(data, 
    options: NSJSONReadingOptions(0), error: &jsonErrorOptional)
    
    // 如果我們不能解析 JSON,就將發(fā)送回去一個(gè)錯(cuò)誤
    if let err = jsonErrorOptional {
      callback(.Left(err))
      return
    }
    
    if let json = jsonOptional as? JSONDictionary {
      if let id = json["id"] as AnyObject? as? Int {
        if let name = json["name"] as AnyObject? as? String {
          if let email = json["email"] as AnyObject? as? String {
            let user = User(id: id, name: name, email: email)
            callback(.Right(user))
            return
          }
        }
      }
    }

    // 如果我們不能解析所有的屬性,就將發(fā)送回去一個(gè)錯(cuò)誤
    callback(.Left(NSError()))
  }
  task.resume()
}

現(xiàn)在調(diào)用getUser的地方可以直接使用Either,然后對(duì)接收到的用戶對(duì)象進(jìn)行處理,或者直接顯示錯(cuò)誤。

getUser(request) { either in
  switch either {
  case let .Left(error):
    //顯示錯(cuò)誤信息

  case let .Right(user):
    //對(duì)user進(jìn)行操作
  }
}

我們假設(shè)Left一直是NSError,這可以進(jìn)一步簡(jiǎn)化代碼。我們可以使用一個(gè)不同的類型 Result<A> 來(lái)保存我們需要的類型數(shù)據(jù)和錯(cuò)誤信息。它的實(shí)現(xiàn)方式如下:

enum Result<A> {
  case Error(NSError)
  case Value(A)
}

在當(dāng)前的 Swift 版本(Beta 5)中,上面的 Result類型會(huì)造成編譯錯(cuò)誤(譯者注:事實(shí)上,在 Swift 1.2 中還是有錯(cuò)誤)。 Swift 需要知道存儲(chǔ)在enum當(dāng)中數(shù)據(jù)的確切類型。可以通過(guò)創(chuàng)建一個(gè)靜態(tài)類作為包裝類型來(lái)解決這個(gè)問(wèn)題:

final class Box<A> {
  let value: A

  init(_ value: A) {
    self.value = value
  }
}

enum Result<A> {
  case Error(NSError)
  case Value(Box<A>)
}

Either 替換為 Result,代碼將變成這樣:

func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request) 
  { data, urlResponse, error in
    // 如果響應(yīng)返回錯(cuò)誤,我們將把錯(cuò)誤發(fā)送給回調(diào)
    if let err = error {
      callback(.Error(err))
      return
    }

    var jsonErrorOptional: NSError?
    let jsonOptional: JSON! =
     NSJSONSerialization.JSONObjectWithData(data, 
     options: NSJSONReadingOptions(0), error: &jsonErrorOptional)
    
    // 如果我們不能解析 JSON,就返回一個(gè)錯(cuò)誤
    if let err = jsonErrorOptional {
      callback(.Error(err))
      return
    }

    if let json = jsonOptional as? JSONDictionary {
      if let id = json["id"] as AnyObject? as? Int {
        if let name = json["name"] as AnyObject? as? String {
          if let email = json["email"] as AnyObject? as? String {
            let user = User(id: id, name: name, email: email)
            callback(.Value(Box(user)))
            return
          }
        }
      }
    }

    // 如果我們不能解析所有的屬性,就返回一個(gè)錯(cuò)誤
    callback(.Error(NSError()))
  }
  task.resume()
}
getUser(request) { result in
  switch result {
  case let .Error(error):
    // 顯示錯(cuò)誤信息

  case let .Value(boxedUser):
    let user = boxedUser.value
    // 對(duì) user 繼續(xù)操作
  }
}

改變不是很大,我們繼續(xù)努力。

重構(gòu): 消除多層嵌套

接下來(lái),我們將為每個(gè)不同的類型創(chuàng)建一個(gè) JSON 解析器來(lái)消滅掉那些丑陋的解析 JSON 的代碼。在這個(gè)對(duì)象中我們只用到了 String, IntDictionary 三種類型,所以我們需要三個(gè)函數(shù)來(lái)對(duì)這三種類型進(jìn)行解析。

func JSONString(object: JSON?) -> String? {
  return object as? String
}

func JSONInt(object: JSON?) -> Int? {
  return object as? Int
}

func JSONObject(object: JSON?) -> JSONDictionary? {
  return object as? JSONDictionary
}

現(xiàn)在,解析 JSON 的代碼看起來(lái)應(yīng)該是這樣的:

if let json = JSONObject(jsonOptional) {
  if let id = JSONInt(json["id"]) {
    if let name = JSONString(json["name"]) {
      if let email = JSONString(json["email"]) {
        let user = User(id: id, name: name, email: email)
      }
    }
  }
}

即使使用了這些函數(shù),還是需要用到一大堆的 if-let 語(yǔ)句。函數(shù)式編程中的 Monads,Applicative Functors,以及 Currying 概念可以幫助我們來(lái)壓縮這段代碼。首先看看與 Swift 中的可選類型十分相似的 Monad。Monad 中有一個(gè)綁定(bind)運(yùn)行符,這個(gè)運(yùn)行符可以給一個(gè)可選類型綁定一個(gè)函數(shù),這個(gè)函數(shù)接受一個(gè)非可選類型參數(shù),并返回一個(gè)可選類型的返回值。如果第一個(gè)可選類型是 .None這個(gè)運(yùn)行符會(huì)返回 .None ,否則它會(huì)對(duì)這個(gè)可選類型進(jìn)行解包,并使用綁定的函數(shù)調(diào)用解包后的數(shù)據(jù)。

infix operator >>> { associativity left precedence 150 }

func >>><A, B>(a: A?, f: A -> B?) -> B? {
  if let x = a {
    return f(x)
  } else {
    return .None
  }
}

在其它的函數(shù)式語(yǔ)言中,都是使用 >>= 來(lái)作為綁定(bind)運(yùn)算符,但是在 Swift 中這個(gè)運(yùn)算符被用于二進(jìn)制位的移位操作,所以我們使用了 >>> 來(lái)作為替代。在 JSON 代碼中使用這個(gè)操作符可以得到如下代碼:

if let json = jsonOptional >>> JSONObject {
  if let id = json["id"] >>> JSONInt {
    if let name = json["name"] >>> JSONString {
      if let email = json["email"] >>> JSONString {
        let user = User(id: id, name: name, email: email)
      }
    }
  }
}

接著就可以去掉解析函數(shù)里的可選參數(shù):

func JSONString(object: JSON) -> String? {
  return object as? String
}

func JSONInt(object: JSON) -> Int? {
  return object as? Int
}

func JSONObject(object: JSON) -> JSONDictionary? {
  return object as? JSONDictionary
}

Functors 有一個(gè)fmap運(yùn)算符,可以在某些上下文中通過(guò)函數(shù)應(yīng)用到解包后的值上面。Applicative Functors 也有apply運(yùn)算符,可以在某些上下文中通過(guò)解包后的函數(shù)應(yīng)用到解包后的值上面。這里的上下文是一個(gè)包含了值的可選值。這就意味著我們可以使用一個(gè)能夠帶有多個(gè)非可選值的函數(shù)來(lái)連接多個(gè)可選值。如果所有的值都存在,.Some會(huì)得到可選值解包的結(jié)果。如果其中任何值是.None,我們將得到.None。可以在 Swift 中像下面這樣定義這些運(yùn)算符:

infix operator <^> { associativity left } // Functor's fmap (usually <$>)
infix operator <*> { associativity left } // Applicative's apply

func <^><A, B>(f: A -> B, a: A?) -> B? {
  if let x = a {
    return f(x)
  } else {
    return .None
  }
}

func <*><A, B>(f: (A -> B)?, a: A?) -> B? {
  if let x = a {
    if let fx = f {
      return fx(x)
    }
  }
  return .None
}

先別著急使用這些代碼,由于 Swift 不支持自動(dòng)柯里化(auto-currying), 我們需要手動(dòng)柯里化(curry)結(jié)構(gòu)體User中的init方法??吕锘囊馑际钱?dāng)我們給定一個(gè)函數(shù)的參數(shù)比它原來(lái)的參數(shù)更少時(shí),這個(gè)函數(shù)將返回一個(gè)包含剩余參數(shù)的函數(shù)。我們的User模型將看起來(lái)像這樣:

struct User {
  let id: Int
  let name: String
  let email: String

  static func create(id: Int)(name: String)(email: String) -> User {
    return User(id: id, name: name, email: email)
  }
}

把以上代碼合并到一起,我們的 JSON 解析現(xiàn)在看起來(lái)是這樣的:


if let json = jsonOptional >>> JSONObject {
  let user = User.create <^>
              json["id"]    >>> JSONInt    <*>
              json["name"]  >>> JSONString <*>
              json["email"] >>> JSONString
}

如果我們解析器的任何部分返回.None,那么user就會(huì)是.None。這看起來(lái)已經(jīng)好多了,但是我們還沒(méi)有優(yōu)化完畢。

到目前為止,我們的getUser函數(shù)看起來(lái)像這樣:

func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in
    // 如果響應(yīng)返回錯(cuò)誤,返回錯(cuò)誤
    if let err = error {
      callback(.Error(err))
      return
    }

    var jsonErrorOptional: NSError?
    let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional)

    // 如果我們不能解析 JSON,返回錯(cuò)誤
    if let err = jsonErrorOptional {
      callback(.Error(err))
      return
    }

    if let json = jsonOptional >>> JSONObject {
      let user = User.create <^>
                  json["id"]    >>> JSONInt    <*>
                  json["name"]  >>> JSONString <*>
                  json["email"] >>> JSONString
      if let u = user {
        callback(.Value(Box(u)))
        return
      }
    }

    // 如果我們不能解析所有的屬性,就返回錯(cuò)誤
    callback(.Error(NSError()))
  }
  task.resume()
}

重構(gòu):通過(guò)綁定消除多個(gè)返回

觀察到在上面的函數(shù)中,我們的調(diào)用了callback函數(shù) 4 次。漏掉任何一次都會(huì)制造 bug。我們可以把這個(gè)函數(shù)分解成 3 個(gè)互不相關(guān)的部分,從而消除潛在的 bug 并重構(gòu)這個(gè)函數(shù)。這三個(gè)部分是:解析響應(yīng),解析數(shù)據(jù)為 JSON 和解析 JSON 為User對(duì)象。這些步驟中的每一步都帶有一個(gè)輸入和返回下一個(gè)步驟的輸入或者錯(cuò)誤。綁定我們的Result類型看起來(lái)是一個(gè)不錯(cuò)的方案。
parseResponse函數(shù)需要Result數(shù)據(jù)和響應(yīng)的狀態(tài)碼。iOS API 只提供了NSURLResponse并保證數(shù)據(jù)獨(dú)立。所以我們創(chuàng)建一個(gè)小結(jié)構(gòu)體來(lái)輔助一下:

struct Response {
  let data: NSData
  let statusCode: Int = 500

  init(data: NSData, urlResponse: NSURLResponse) {
    self.data = data
    if let httpResponse = urlResponse as? NSHTTPURLResponse {
      statusCode = httpResponse.statusCode
    }
  }
}

現(xiàn)在我們可以把Response結(jié)構(gòu)體傳入parseResponse函數(shù),然后在處理數(shù)據(jù)之前處理錯(cuò)誤。

func parseResponse(response: Response) -> Result<NSData> {
  let successRange = 200..<300
  if !contains(successRange, response.statusCode) {
    return .Error(NSError()) // 自定義你想要的錯(cuò)誤信息
  }
  return .Value(Box(response.data))
}

下一個(gè)函數(shù)需要我們將一個(gè)可選值轉(zhuǎn)換成Result類型,我們先來(lái)抽象一下。

func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> {
  if let a = optional {
    return .Value(Box(a))
  } else {
    return .Error(error)
  }
}

接下來(lái)的函數(shù)需要解析數(shù)據(jù)為 JSON:

func decodeJSON(data: NSData) -> Result<JSON> {
  let jsonOptional: JSON! = 
  NSJSONSerialization.JSONObjectWithData(data, 
  options: NSJSONReadingOptions(0), error: &jsonErrorOptional)
  return resultFromOptional(jsonOptional, NSError()) 
  // 使用默認(rèn)的錯(cuò)誤或者自定義錯(cuò)誤信息
}

然后,我們?cè)?code>User類型中添加 JSON 到User類型的轉(zhuǎn)換:

struct User {
  let id: Int
  let name: String
  let email: String

  static func create(id: Int)(name: String)(email: String) -> User {
    return User(id: id, name: name, email: email)
  }

  static func decode(json: JSON) -> Result<User> {
    let user = JSONObject(json) >>> { dict in
      User.create <^>
          dict["id"]    >>> JSONInt    <*>
          dict["name"]  >>> JSONString <*>
          dict["email"] >>> JSONString
    }
    return resultFromOptional(user, NSError()) // 自定義錯(cuò)誤消息
  }
}

合并代碼之前,需要擴(kuò)展一下綁定, 讓>>>來(lái)配合Result類型:

func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> {
  switch a {
  case let .Value(x):     return f(x.value)
  case let .Error(error): return .Error(error)
  }
}

然后我們添加一個(gè)Result的自定義構(gòu)造器:

enum Result<A> {
  case Error(NSError)
  case Value(Box<A>)

  init(_ error: NSError?, _ value: A) {
    if let err = error {
      self = .Error(err)
    } else {
      self = .Value(Box(value))
    }
  }
}

現(xiàn)在我們可以把所有的函數(shù)使用綁定運(yùn)算符連接到一起了:

func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in
    let responseResult = Result(error, 
    Response(data: data, urlResponse: urlResponse))
    let result = responseResult >>> parseResponse
                                >>> decodeJSON
                                >>> User.decode
    callback(result)
  }
  task.resume()
}

Wow,即使再次書寫這些代碼,我都對(duì)這些結(jié)果感到興奮。你可能會(huì)想,"這已經(jīng)非??犰帕?,我們已經(jīng)迫不及待的想用它了!",但是這還不算完!

重構(gòu):使用泛型抽象類型

已經(jīng)非常棒了,但是我們?nèi)匀幌刖帉戇@個(gè)解析器適用于任何類型。我可以使用泛型(Generics)來(lái)使得解析器完全抽象。

我們引入JSONDecodable協(xié)議,讓上面的類型遵守它。協(xié)議看起來(lái)是這樣的:

protocol JSONDecodable {
  class func decode(json: JSON) -> Self?
}

然后,我們編寫一個(gè)函數(shù),解析任何遵守JSONDecodable協(xié)議的類型為Result類型:

func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> {
  return resultFromOptional(A.decode(json), NSError()) // 自定義錯(cuò)誤
}

現(xiàn)在我們可以讓User遵守協(xié)議:

struct User: JSONDecodable {
  let id: Int
  let name: String
  let email: String

  static func create(id: Int)(name: String)(email: String) -> User {
    return User(id: id, name: name, email: email)
  }

  static func decode(json: JSON) -> User? {
    return JSONObject(json) >>> { d in
      User.create <^>
        d["id"]    >>> JSONInt    <*>
        d["name"]  >>> JSONString <*>
        d["email"] >>> JSONString
  }
}

我們改變了User的解析函數(shù),用可選的User替換掉Result<User>。這樣我們就擁有了一個(gè)抽象的函數(shù),可以在解碼后調(diào)用resultFromOptional,替代之前模型中必須使用的decode函數(shù)。

最后,我們抽象performRequest函數(shù)中的解析和解碼過(guò)程,讓它們變得更加易讀。下面是最終的performRequestparseResult函數(shù):

func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) {
  let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in
    callback(parseResult(data, urlResponse, error))
  }
  task.resume()
}

func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> {
  let responseResult = Result(error, Response(data: data, urlResponse: urlResponse))
  return responseResult >>> parseResponse
                        >>> decodeJSON
                        >>> decodeObject
}

繼續(xù)學(xué)習(xí)

實(shí)例代碼放在了GitHub上供下載
如果你對(duì)函數(shù)式編程或者這篇文章討論的任何概念感興趣,請(qǐng)查閱Haskell編程語(yǔ)言和Learn You a Haskell書中的一篇特定文章,同時(shí),請(qǐng)查閱Pat Brisbin寫的博客:Applicative Options Parsing in Haskell

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,650評(píng)論 4 61
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,694評(píng)論 19 139
  • 生活就像一塊巧克力,你永遠(yuǎn)不知道它到底有多黑。 生活如一片死水,她出生了,上學(xué)了,被欺負(fù)了,又在眾多否定的聲音中考...
    優(yōu)票票閱讀 148評(píng)論 0 0
  • 驀然回首三十載, 一事無(wú)成心自憐。 雄心猶在已無(wú)志, 兀自空想花已殘。 人生匆忙至而立,猛然回首事無(wú)成; 呆坐空想...
    閑居散人閱讀 926評(píng)論 0 5
  • 風(fēng)起龍飛閱讀 273評(píng)論 0 13

友情鏈接更多精彩內(nèi)容