使用 Swift 解析 JSON

本文翻译自这篇文章,本文中所有的代码都放在Github

我将在本文中概述一个使用 Swift 完成的处理 JSON 的解析库。一个 JSON 的例子如下:

  1. var json : [String: AnyObject] = [
  2. "stat": "ok",
  3. "blogs": [
  4. "blog": [
  5. [
  6. "id" : 73,
  7. "name" : "Bloxus test",
  8. "needspassword" : true,
  9. "url" : "http://remote.bloxus.com/"
  10. ],
  11. [
  12. "id" : 74,
  13. "name" : "Manila Test",
  14. "needspassword" : false,
  15. "url" : "http://flickrtest1.userland.com/"
  16. ]
  17. ]
  18. ]
  19. ]

最具挑战的部分就是如何将该数据转换成如下 Swift 结构体的数组:

  1. struct Blog {
  2. let id: Int
  3. let name: String
  4. let needsPassword : Bool
  5. let url: NSURL
  6. }

我们首先来看最终的解析函数,它包含两个运算法:>>= 和 <*> 。这两个运算符或许看起来很陌生,但是解析整个 JSON 结构就是这么简单。本文其他部分将会解释这些库代码。下面的解析代码是这样工作的:如果 JSON 是不合法的(比如 name 不存在或者 id 不是整型)最终结果将是 nil 。我们不需要反射(reflection)和 KVO ,仅仅需要几个函数和一些聪明的组合方式:

  1. func parseBlog(blog: AnyObject) -> Blog? {
  2. return asDict(blog) >>= {
  3. mkBlog <*> int($0,"id")
  4. <*> string($0,"name")
  5. <*> bool($0,"needspassword")
  6. <*> (string($0, "url") >>= toURL)
  7. }
  8. }
  9. let parsed : [Blog]? = dictionary(json, "blogs") >>= {
  10. array($0, "blog") >>= {
  11. join($0.map(parseBlog))
  12. }
  13. }

上面的代码到底做了什么呢?我们来仔细看看这些最重要的函数。首先来看看 dictionary 函数,它接受一个 String 到 AnyObject 的字典,返回另一个具有指定 key 的字典:

  1. dictionary(input: [String: AnyObject], key: String) ->  [String: AnyObject]? {
  2. return input[key] >>= { $0 as? [String:AnyObject] }
  3. }

例如在前面的 JSON 例子中,我们期望 key = “blogs” 包含一个字典。如果字典存在,上述函数返回该字典,否则返回 nil 。我们可以对 Array、String、Integer 写出同样的方法(下面只是生命,完整代码见 Github):

  1. func array(input: [String:AnyObject], key: String) ->  [AnyObject]?
  2. func string(input: [String:AnyObject], key: String) -> String?
  3. func int(input: [NSObject:AnyObject], key: String) -> Int?

现在,我们来看一下 JSON 例子的完整结构。它本身就是一个字典,包含一个 key 为 “blogs” 的另一个字典。该字典包含一个 key 为 “blog” 的 Array 。我们可以用下面的代码表达上述结构:

  1. if let blogsDict = dictionary(parsedJSON, "blogs") {
  2. if let blogsArray = array(blogsDict, "blog") {
  3. // Do something with the blogs array
  4. }
  5. }

我么可以实现一个 >>= 操作来代替,接受一个 optional 参数,当该参数不为 nil 的时候,对其使用一个函数。该操作符使用 flatten 函数,flatten 函数将嵌套的 optional 展开:

  1. operator infix >>= {}
  2. @infix func >>= <u,t>(optional : T?, f : T -> U?) -> U? {
  3. return flatten(optional.map(f))
  4. }
  5. func flatten(x: A??) -> A? {
  6. if let y = x { return y }
  7. return nil
  8. }

另一个被频繁使用的是 <*> 操作符。例如下面的代码是用来解析单个 blog 的:

  1. mkBlog <*> int(dict,"id")
  2. <*> string(dict,"name")
  3. <*> bool(dict,"needspassword")
  4. <*> (string(dict, "url") >>= toURL)

当所有的 optional 参数都是 non-nil 的时候该函数才能正常运行,上面的代码转化成:

  1. mkBlog(int(dict,"id"), string(dict,"name"), bool(dict,"needspassword"), (string(dict, "url") >>= toURL))

所以,我们来看看操作符 <*> 的定义。它接受两个 optional 的参数,左边的参数是一个函数。如果两个参数都不是 nil ,将会对右边的参数使用左边的函数参数:

  1. operator infix <*> { associativity left precedence 150 }
  2. func <*><a, b>(f: (A -> B)?, x: A?) -> B? {
  3. if let f1 = f {
  4. if let x1 = x {
  5. return f1(x1)
  6. }
  7. }
  8. return nil
  9. }

现在你有可能想知道 mkBlog 是做什么的吧。它是一个 curried 函数用来包装我们的初始化函数。首先,我们有一个 (Int,String,Bool,NSURL) –> Blog 类型的函数。然后 curry 函数将其类型转化为 Int -> String -> Bool -> NSURL -> Blog :

  1. let mkBlog = curry {id, name, needsPassword, url in
  2. Blog(id: id, name: name, needsPassword: needsPassword, url: url)
  3. }

我们将 mkBlog 和 <*> 一起使用,我们来看第一行:

  1. // mkBlog : Int -> String -> Bool -> NSURL -> Blog
  2. // int(dict,"id") : Int?
  3. let step1 = mkBlog <*> int(dict,"id")

可以看到,用 <*> 将他们两个连起来,将会返回一个新的类型:(String -> Bool -> NSURL -> Blog)? ,然后和 string 函数结合:

  1. let step2 = step1 <*> string(dict,"name")

我们得到:(Bool -> NSURL -> Blog)? ,一直这样结合,最后将会得到类型为 Blog? 的值。

希望你现在能明白整个代码是如何在一起工作的了。通过创建一些辅助函数和运算符,我们可以让解析强类型的 JSON 数据变得非常容易。如果不用 optional 类型,那么我们将会使用完全不同的类型,并且包含一些错误信息,但这将是另外的 blog 的话题了。

时间: 2024-11-05 16:01:17

使用 Swift 解析 JSON的相关文章

swift解析json数据

1 // 2 // ViewController.swift 3 // JieParseJson 4 // 5 // Created by jiezhang on 14-10-5. 6 // Copyright (c) 2014年 jiezhang. All rights reserved. 7 // 8 9 import UIKit 10 11 class ViewController: UIViewController { 12 13 @IBOutlet weak var city: UIL

iOS开发——网络Swift篇&amp;JSON与XML数据解析

JSON与XML数据解析 JSON数据解析(内置NSJSONSerialization与第三方JSONKit) 一,使用自带的NSJSONSerialization 苹果从IOS5.0后推出了SDK自带的JSON解决方案NSJSONSerialization,这是一个非常好用的JSON生成和解析工具,效率也比其他第三方开源项目高. NSJSONSerialization能将JSON转换成Foundation对象,也能将Foundation对象转换成JSON,但转换成JSON的对象必须具有如下属性

SWIFT解析天气JSON格式

访问以下链接可以得到京城当天的天气:http://www.weather.com.cn/adat/sk/101010100.html 返回的JSON格式如下: {"weatherinfo":{"city":"北京","cityid":"101010100","temp":"9","WD":"西南风","WS"

swift NSJSONSerialization json解析

以下是解析.生成方法 func jsonParse(){ /******************************************  解析json类型  **************************************/ let jsonFile = NSBundle.mainBundle().pathForResource("JsonParseFile", ofType: "geojson") let jsonData = NSData.

Swift: Alamofire -&gt; http请求 &amp; ObjectMapper -&gt; 解析JSON

1 2 3 4 5 6 7 8 9 10 11 NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"]; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; [manager GET:URL.absoluteString parameters:nil     progress:nil     success:^(NS

Swift: 用Alamofire做http请求,用ObjectMapper解析JSON

演示样例代码看最后. 跟不上时代的人突然间走在了时代的前列,果然有别样的风景.首先歧视一下AFNetworking.这个东西实在太难用了.不想封装都不行,要不写一大堆代码. NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"]; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; [manager GET:URL.

ios swift http json

// // ViewController.swift // http_request // // Created by andy on 14-9-6. // Copyright (c) 2014年 andy. All rights reserved. // import UIKit class ViewController: UIViewController { @IBOutlet var btn:UIButton? @IBOutlet var lable:UILabel? override f

JAVA解析JSON数据

转自:http://www.cnblogs.com/boy1025/p/4551593.html 3.解析JSON数据(小编使用的GSON进行json数据的解析) 3-1 [JSONObject的解析] 下面是一个json文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

安卓中解析json数据

一.概述 JSON是JavaScript Object Notation的简称,起源于js(javascript)它是一种轻量级的数据交换格式,JSON不仅在js中广泛使用,同时还在其他领域得到广泛使用,如c,c++,java,Php,swift等等,成为了一种通用的理想数据交换格式,它有两种数据结构,分别是对象,数组,它形式上有花括号{}和中括号[]嵌套,{}中的是代表对象,[]中的为数组,即对象中有数组,数组中又有对象,而且以及键/值对出现. json语法: 数据在键值对中 数据有逗号分离