SwiftyJSON详解

最近看了一些网络请求的例子,发现Swift在解析JSON数据时特别别扭,总是要写一大堆的downcast(as?)和可选(Optional),看?号都看花了。随后发现了这个库SwiftyJSON,问题迎刃而解,灰常优雅和Swifty!

简单介绍下这个库(内容译自SwiftyJSONREADME):

为什么典型的在Swift中处理JSON的方法不好?

Swift语言是一种严格的类型安全语言,它要求我们显示的设置类型,并帮助我们写出更少bug的代码。但是当处理JSON这种天生就是隐式类型的数据结构,就非常麻烦了。

拿Twitter中timeline API返回的数据为例:

  1. [
  2. {
  3. ......
  4. "text": "just another test",
  5. ......
  6. "user": {
  7. "name": "OAuth Dancer",
  8. ,
  9. "entities": {
  10. "url": {
  11. "urls": [
  12. {
  13. "expanded_url": null,
  14. "url": "http://bit.ly/oauth-dancer",
  15. "indices": [
  16. ,
  17.  
  18. ],
  19. "display_url": null
  20. }
  21. ]
  22. }
  23. ......
  24. },
  25. "in_reply_to_screen_name": null,
  26. },
  27. ......]

Swift中的解析代码会是这样:

  1. let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(dataFromTwitter, options: NSJSONReadingOptions.MutableContainers, error: nil)
  2. if let statusesArray = jsonObject as? NSArray{
  3. ] as? NSDictionary{
  4. if let user = aStatus["user"] as? NSDictionary{
  5. if let userName = user["name"] as? NSDictionary{
  6. //终于我们得到了`name`
  7.  
  8. }
  9. }
  10. }
  11. }

不好吧。就算是换成可选链式调用,也还是一团糟:

  1. let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(dataFromTwitter, options: NSJSONReadingOptions.MutableContainers, error: nil)
  2. ] as? NSDictionary)?["user"] as? NSDictionary)?["name"]{
  3. //上面这一堆是个啥??
  4. }

使用SwiftyJSON

你只要这样做就行了:

  1. let json = JSONValue(dataFromNetworking)
  2. if let userName = json[0]["user"]["name"].string{
  3. //恩~ `name`到手,就这么简单
  4. }

你不需要考虑可选类型的拆包和是否能拆包的判断,这些都自动完成了:

  1. let json = JSONValue(dataFromNetworking)
  2. ]["wrong_key"]["wrong_name"].string{
  3. //冷静,嘿嘿~ 调用不存在的["wrong_key]也不会crash滴, .string最终能安全的返回一个字符串或`nil`
  4. }
  5.  
  6. let json = JSONValue(jsonObject)
  7. switch json["user_id"]{
  8. case .JString(let stringValue):
  9. let id = stringValue.toInt()
  10. case .JNumber(let doubleValue):
  11. let id = Int(doubleValue)
  12. default:
  13. println("ooops!!! JSON Data is Unexpected or Broken")

后记:SwiftyJSON是怎么做到的?

看到这个库之后,一方面很爽终于有合适的处理JSON的方法了;另一方面心里其实很好奇它是怎么做到的?

通过看源代码,才了解到它是创建了一个JSONValue枚举,这个枚举中有一个JInvalid类型。当使用json字符串来构造JSONValue对象时,如果无法构建成功,就会返回这个JInvalid枚举对象,然后对这个JInvalid枚举对象继续处理,会继续返回JInvalid。直到对其调用string, number, bool之类来获取Swift中的数据类型值时,才会返回nil

这套机制是类似于Optional<T>可选类型的,但是不同的是,Optional中对nil调用方法会crash,但JSONValue中对JInvalid调用方法不会crash,而是继续返回JInvalid。这样使用时就不用写一堆?号啦,反正不会出错滴。

同时,它给JSONValue枚举还创建了其它json中使用到的各种类型JNumber, JString, JBool,它们能通过构造器将原始值包装起来,然后最后通过对应的numberstringbool等属性方法来拆包,得到原始值。

推荐大家也读读这个库的源代码,其对enum的使用灰常巧妙!

json-swift 和 SwiftyJSON 的比较

最近微博上 @SwiftLanguage 让我对这两个库做个简单比较,所以就有了下文:

json-swiftSwiftyJSON都使用了一个自定义的枚举类型来描述JSON数据;通过重载实现了类似Array和Dictionary的下标操作;并可以将NSData类型的json实例转换成其对应的枚举类型的实例。

它们都解决了原来访问JSON类型数据时,必须手动downcast的繁琐操作,如原来要json[“blogs”]? as? Array,现在json[“blogs”]?即可。SwiftyJSON更进一步,连?也可以省掉,使用上更接近Objective-C风格;json-swift保留了?,跟Swift整体风格一致,此外还提供了直接从字面值实例化的便捷操作。

关于用不用?,我个人倾向于SwiftyJSON的做法。其最大优势是可以省写很多?号,无论写程序还是看程序都变得更简单直观;同时由于json结构中数据类型本身就是动态的,如果把每次取值当做一次操作,那么取值的过程不那么type-safe我认为可以接受,只要最终能保证取值结果跟Swift兼容(可选类型)。

if you love it, please page to :https://github.com/SwiftyJSON/SwiftyJSON

  1. Why is the typical JSON handling in Swift NOT good?
  2.  
  3. Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types.
  4.  
  5. Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to Twitter's API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline).
  6.  
  7. The code would look like this:
  8.  
  9. let JSONObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
  10.  
  11. if let statusesArray = JSONObject as? [AnyObject],
  12. let status = statusesArray[] as? [String: AnyObject],
  13. let user = status["user"] as? [String: AnyObject],
  14. let username = user["name"] as? String {
  15. // Finally we got the username
  16. }
  17.  
  18. It's not good.
  19.  
  20. Even if we use optional chaining, it would be messy:
  21.  
  22. let JSONObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
  23.  
  24. ] as? [String: AnyObject])?["user"] as? [String: AnyObject])?["name"] as? String {
  25. // What a disaster
  26. }
  27.  
  28. An unreadable mess--for something that should really be simple!
  29.  
  30. With SwiftyJSON all you have to do is:
  31.  
  32. let json = JSON(data: dataFromNetworking)
  33. ]["user"]["name"].string{
  34. //Now you got your value
  35. }
  36.  
  37. And don't worry about the Optional Wrapping thing. It's done for you automatically.
  38.  
  39. let json = JSON(data: dataFromNetworking)
  40. ]["wrong_key"]["wrong_name"].string{
  41. //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety
  42. } else {
  43. //Print the error
  44. println(json[]["wrong_key"]["wrong_name"])
  45. }
  46.  
  47. Requirements
  48.  
  49. iOS 7.0+ / Mac OS X 10.9+
  50. Xcode 6.1
  51.  
  52. Integration
  53. CocoaPods (iOS +, OS X 10.9+)
  54.  
  55. You can use Cocoapods to install SwiftyJSONby adding it to your Podfile:
  56.  
  57. platform :ios, '8.0'
  58. use_frameworks!
  59.  
  60. target 'MyApp' do
  61. pod 'SwiftyJSON', '~> 2.2.0'
  62. end
  63.  
  64. Note that it needs you to install CocoaPods version, and requires your iOS deploy target >= 8.0:
  65. Carthage (iOS +, OS X 10.9+)
  66.  
  67. You can use Carthage to install SwiftyJSON by adding it to your Cartfile:
  68.  
  69. github
  70.  
  71. Manually (iOS +, OS X 10.9+)
  72.  
  73. To use this library in your project manually you may:
  74.  
  75. for Projects, just drag SwiftyJSON.swift to the project tree
  76. for Workspaces, include the whole SwiftyJSON.xcodeproj
  77.  
  78. Usage
  79. Initialization
  80.  
  81. import SwiftyJSON
  82.  
  83. let json = JSON(data: dataFromNetworking)
  84.  
  85. let json = JSON(jsonObject)
  86.  
  87. if let dataFromString = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
  88. let json = JSON(data: dataFromString)
  89. }
  90.  
  91. Subscript
  92.  
  93. //Getting a double from a JSON Array
  94. let name = json[].double
  95.  
  96. //Getting a string from a JSON Dictionary
  97. let name = json["name"].stringValue
  98.  
  99. //Getting a string using a path to the element
  100. let path = [,,"name"]
  101. let name = json[path].string
  102. //Just the same
  103. let name = json[][]["name"].string
  104. //Alternatively
  105. let name = json[,,"name"].string
  106.  
  107. //With a hard way
  108. let name = json[].string
  109.  
  110. //With a custom way
  111. let keys:[SubscriptType] = [,,"name"]
  112. let name = json[keys].string
  113.  
  114. Loop
  115.  
  116. //If json is .Dictionary
  117. for (key: String, subJson: JSON) in json {
  118. //Do something you want
  119. }
  120.  
  121. The first element is always a String, even if the JSON is an Array
  122.  
  123. //If json is .Array
  124. //The `index` is 0..<json.count's string value
  125. for (index: String, subJson: JSON) in json {
  126. //Do something you want
  127. }
  128.  
  129. Error
  130.  
  131. Use a subscript to get/set a value in an Array or Dictionary
  132.  
  133. If the json is:
  134.  
  135. an array, the app may crash with "index out-of-bounds."
  136. a dictionary, it will get nil without a reason.
  137. not an array or a dictionary, the app may crash with an "unrecognised selector" exception.
  138.  
  139. It will never happen in SwiftyJSON.
  140.  
  141. let json = JSON(["name", "age"])
  142. ].string {
  143. //Do something you want
  144. } else {
  145. println(json[].error) // "Array[999] is out of bounds"
  146. }
  147.  
  148. let json = JSON([])
  149. if let name = json["address"].string {
  150. //Do something you want
  151. } else {
  152. println(json["address"].error) // "Dictionary["address"] does not exist"
  153. }
  154.  
  155. let json = JSON()
  156. ].string {
  157. //Do something you want
  158. } else {
  159. println(json[]) // "Array[0] failure, It is not an array"
  160. println(json[].error) // "Array[0] failure, It is not an array"
  161. }
  162.  
  163. if let name = json["name"].string {
  164. //Do something you want
  165. } else {
  166. println(json["name"]) // "Dictionary[\"name"] failure, It is not an dictionary"
  167. println(json["name"].error) // "Dictionary[\"name"] failure, It is not an dictionary"
  168. }
  169.  
  170. Optional getter
  171.  
  172. //NSNumber
  173. if let id = json["user"]["favourites_count"].number {
  174. //Do something you want
  175. } else {
  176. //Print the error
  177. println(json["user"]["favourites_count"].error)
  178. }
  179.  
  180. //String
  181. if let id = json["user"]["name"].string {
  182. //Do something you want
  183. } else {
  184. //Print the error
  185. println(json["user"]["name"])
  186. }
  187.  
  188. //Bool
  189. if let id = json["user"]["is_translator"].bool {
  190. //Do something you want
  191. } else {
  192. //Print the error
  193. println(json["user"]["is_translator"])
  194. }
  195.  
  196. //Int
  197. if let id = json["user"]["id"].int {
  198. //Do something you want
  199. } else {
  200. //Print the error
  201. println(json["user"]["id"])
  202. }
  203. ...
  204.  
  205. Non-optional getter
  206.  
  207. Non-optional getter is named xxxValue
  208.  
  209. //If not a Number or nil, return 0
  210. let id: Int = json["id"].intValue
  211.  
  212. //If not a String or nil, return ""
  213. let name: String = json["name"].stringValue
  214.  
  215. //If not a Array or nil, return []
  216. let list: Array<JSON> = json["list"].arrayValue
  217.  
  218. //If not a Dictionary or nil, return [:]
  219. let user: Dictionary<String, JSON> = json["user"].dictionaryValue
  220.  
  221. Setter
  222.  
  223. json["name"] = JSON("new-name")
  224. json[] = JSON()
  225.  
  226. json[
  227. json["coordinate"].double = 8766.766
  228. json["name"].string = "Jack"
  229. json.arrayObject = [,,,]
  230. json.dictionary = []
  231.  
  232. Raw object
  233.  
  234. let jsonObject: AnyObject = json.object
  235.  
  236. if let jsonObject: AnyObject = json.rawValue
  237.  
  238. //convert the JSON to raw NSData
  239. if let data = json.rawData() {
  240. //Do something you want
  241. }
  242.  
  243. //convert the JSON to a raw String
  244. if let string = json.rawString() {
  245. //Do something you want
  246. }
  247.  
  248. Literal convertibles
  249.  
  250. For more info about literal convertibles: Swift Literal Convertibles
  251.  
  252. //StringLiteralConvertible
  253. let json: JSON = "I'm a json"
  254.  
  255. //IntegerLiteralConvertible
  256. let json: JSON =
  257.  
  258. //BooleanLiteralConvertible
  259. let json: JSON = true
  260.  
  261. //FloatLiteralConvertible
  262. let json: JSON = 2.8765
  263.  
  264. //DictionaryLiteralConvertible
  265. let json: JSON = ["I":"am", "a":"json"]
  266.  
  267. //ArrayLiteralConvertible
  268. let json: JSON = ["I", "am", "a", "json"]
  269.  
  270. //NilLiteralConvertible
  271. let json: JSON = nil
  272.  
  273. //With subscript in array
  274. var json: JSON = [,,]
  275. json[] =
  276. json[] =
  277. json[] =
  278. json[] = //Don't worry, nothing will happen
  279.  
  280. //With subscript in dictionary
  281. var json: JSON = []
  282. json["name"] = "Mike"
  283. json[" //It's OK to set String
  284. json["address"] = "L.A." // Add the "address": "L.A." in json
  285.  
  286. //Array & Dictionary
  287. var json: JSON = [, "list": ["a", "b", "c", ["what": "this"]]]
  288. json[]["what"] = "that"
  289. json[,"what"] = "that"
  290. let path = [,"what"]
  291. json[path] = "that"
  292.  
  293. Work with Alamofire
  294.  
  295. SwiftyJSON nicely wraps the result of the Alamofire JSON response handler:
  296.  
  297. Alamofire.request(.GET, url, parameters: parameters)
  298. .responseJSON { (req, res, json, error) in
  299. if(error != nil) {
  300. NSLog("Error: \(error)")
  301. println(req)
  302. println(res)
  303. }
  304. else {
  305. NSLog("Success: \(url)")
  306. var json = JSON(json!)
  307. }
  308. }

iOS开发——网络编程Swift篇&(八)SwiftyJSON详解的更多相关文章

  1. iOS开发——网络编程Swift篇&Alamofire详解

    Alamofire详解 预览图 Swift Alamofire 简介 Alamofire是 Swift 语言的 HTTP 网络开发工具包,相当于Swift实现AFNetworking版本. 当然,AF ...

  2. iOS开发——网络编程Swift篇&(七)NSURLSession详解

    NSURLSession详解 // MARK: - /* 使用NSURLSessionDataTask加载数据 */ func sessionLoadData() { //创建NSURL对象 var ...

  3. iOS开发——网络编程Swift篇&(二)同/异&步请求

    同/异&步请求 同步: // MARK: - 同步请求 func httpSynchronousRequest() { //创建NSURL对象 var url:NSURL! = NSURL(s ...

  4. iOS开发——网络编程Swift篇&(一)网络监测

    网络监测 enum ReachabilityType { case WWAN, WiFi, NotConnected } public class Reachability { /** :see: O ...

  5. iOS开发——网络编程Swift篇&(六)异步Post方式

    异步Post方式 // MARK: - 异步Post方式 func asynchronousPost() { //创建NSURL对象 var url:NSURL! = NSURL(string: &q ...

  6. iOS开发——网络编程Swift篇&(五)同步Post方式

    同步Post方式 // MARK: - 同步Post方式 func synchronousPost() { //创建NSURL对象 var url:NSURL! = NSURL(string: &qu ...

  7. iOS开发——网络编程Swift篇&(四)异步Get方式

    异步Get方式 // MARK: - 异步Get方式 func asynchronousGet() { //创建NSURL对象 var url:NSURL! = NSURL(string: " ...

  8. iOS开发——网络编程Swift篇&(三)同步Get方式

    同步Get方式 // MARK: - 同步Get方式 func synchronousGet() { //创建NSURL对象 var url:NSURL! = NSURL(string: " ...

  9. iOS开发——网络编程OC篇&Socket编程

    Socket编程 一.网络各个协议:TCP/IP.SOCKET.HTTP等 网络七层由下往上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 其中物理层.数据链路层和网络层通常被称作 ...

随机推荐

  1. delphi7 开发布局

    delphi7界面布局(就是各种框框,代码管理器之类的东东)好了的,为什么以打开个新项目或者下次进入开发界面是环境的布局全部变了,如何才能锁定窗口布局? 在菜单栏最右边,有一个按钮save curre ...

  2. ASP.NET 日期 时间 年 月 日 时 分 秒 格式及转换

    在平时编码中,经常要把日期转换成各种各样的形式输出或保持,今天专门做了个测试,发现DateTime的ToString()方法居然有这么多的表现形式,和大家一起分享. DateTime time=Dat ...

  3. Claim-based-security for ASP.NET Web APIs using DotNetOpenAuth

    Recently I worked with a customer assisting them in implementing their Web APIs using the new ASP.NE ...

  4. Android Environment 类详解

    Android应用开发中,常使用Environment类去获取外部存储目录,在访问外部存储之前一定要先判断外部存储是否已经是可使用(已挂载&可使用)状态, 并且需要在AndroidManife ...

  5. CSS布局基础

    (初级)css布局 一.单列布局1.基础知识块级元素 div p ul li dl dt 行级元素 img span input strong同一行显示.无换行2.盒子模型盒子模型 (边框border ...

  6. Microsoft TFS 如何显示在Windows 的上下文菜单中

    How to showing in Windows Explorer context for TFS I am not sure if this would help or you are willi ...

  7. Linux下文件的压缩与打包

    一.Linux下常见的文件压缩命令: 在Linux的环境中,压缩文件的扩展名大多是:『*.tar, *.tar.gz, *.tgz, *.gz, *.Z, *.bz2』,为什么会有这样的扩展名呢? 这 ...

  8. codeforces 624A Save Luke(水题)

    A. Save Luke time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  9. 用ALAssetsLibrary将过滤后图片写入照片库

    转载自:http://blog.sina.com.cn/s/blog_61235faa0100z3dp.html CIImage *saveToSave = [filter outputImage]; ...

  10. HDU1398Square Coins(母函数)

    母函数介绍见另一篇随笔HDU1028Ignatius and the Princess III(母函数) #include<iostream> #include<stdio.h> ...