JSON

一段简单的JSON解析代码到底能重构几次?(4)

字号+ 作者:H5之家 来源:H5之家 2015-10-08 10:10 我要评论( )

先别着急使用这些代码,由于Swift不支持自动柯里化(auto-currying),我们需要手动柯里化(curry)结构体User中的init方法。柯里化的意思是当我们给定一个函数的参数比它原来的参数更少时,这个函数将返回一个包含

先别着急使用这些代码,由于Swift不支持自动柯里化(auto-currying),我们需要手动柯里化(curry)结构体User中的init方法。柯里化的意思是当我们给定一个函数的参数比它原来的参数更少时,这个函数将返回一个包含剩余参数的函数。我们的User模型将看起来像这样:

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解析现在看起来是这样的:

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

如果我们解析器的任何部分返回.None,那么user就会是.None。这看起来已经好多了,但是我们还没有优化完毕。

到目前为止,我们的getUser函数看起来像这样:

func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // 如果响应返回错误,返回错误 if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // 如果我们不能解析 JSON,返回错误 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 } } // 如果我们不能解析所有的属性,就返回错误 callback(.Error(NSError())) } task.resume() }


重构:通过绑定消除多个返回

观察到在上面的函数中,我们的调用了callback函数4次。漏掉任何一次都会制造Bug。我们可以把这个函数分解成3个互不相关的部分,从而消除潜在的Bug并重构这个函数。这三个部分是:解析响应,解析数据为 JSON和解析JSON为User对象。这些步骤中的每一步都带有一个输入和返回下一个步骤的输入或者错误。绑定我们的Result类型看起来是一个不错的方案。 parseResponse函数需要Result数据和响应的状态码。iOS API只提供了NSURLResponse并保证数据独立。所以我们创建一个小结构体来辅助一下:

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 } } }

现在我们可以把Response结构体传入parseResponse函数,然后在处理数据之前处理错误。

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

下一个函数需要我们将一个可选值转换成Result类型,我们先来抽象一下。

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

接下来的函数需要解析数据为JSON:

func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // 使用默认的错误或者自定义错误信息 }

然后,我们在User类型中添加 JSON 到User类型的转换:

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()) // 自定义错误消息 } }

合并代码之前,需要扩展一下绑定,让>>>来配合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) } }

然后我们添加一个Result的自定义构造器:

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)) } } }现在我们可以把所有的函数使用绑定运算符连接到一起了:
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,即使再次书写这些代码,我都对这些结果感到兴奋。你可能会想,”这已经非常酷炫了,我们已经迫不及待的想用它了!”,但是这还不算完!
重构:使用泛型抽象类型

已经非常棒了,但是我们仍然想编写这个解析器适用于任何类型。我可以使用泛型(Generics)来使得解析器完全抽象。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • iOS入门学习(json解析)

    iOS入门学习(json解析)

    2016-01-26 09:00

  • iOS中json解析出现的null,nil,NSNumber的问题,jsonnsnumber

    iOS中json解析出现的null,nil,NSNumber的问题,jsonnsnumber

    2016-01-17 15:03

  • iOS开发Post请求错误:Error Domain=NSCocoaErrorDomain Code=3840

    iOS开发Post请求错误:Error Domain=NSCocoaErrorDomain Code=3840 "

    2015-11-23 19:03

  • 叶玲童鞋的课程

    叶玲童鞋的课程

    2015-11-23 12:17

网友点评