SwiftでJSONを扱うライブラリ SwiftyJSON | json-swift
SwiftでJSONを扱う「つらみ」を解消するライブラリがトレンドにあがってたのでながめてみます。
つらみ
SwiftでJSONを扱うとなったとき、まず頭に浮かぶのがFoundationフレームワークのNSJSONSerialization
です。
JSONを渡すと、パースしてNSDictionary
やNSArray
のオブジェクトにしてくれます。個々の要素はNSString
,NSNumber
,あるいはNSNull
といったオブジェクトに変換されます。
SwiftではObjective-Cのオブジェクトを扱うことができるので、NSJSONSerialization
を使ってパースしたらSwiftでもJSONを扱えるはずです。
さて、ではコレをつかって書いてみましょう。サンプルにはTwitter APIのサンプルレスポンスを使います。
https://dev.twitter.com/docs/api/1.1/get/users/show
ここから、screen_name
と、status/entities/urls/[0]/url
を取ってみます。
import Foundation let sampleJSONData :NSData = NSData(contentsOfFile: "/path/to/sampleJSON.json") let sampleJSON :Dictionary = NSJSONSerialization.JSONObjectWithData(sampleJSONData, options: nil, error: nil) as NSDictionary var screen_name :String var url :String // Get screen_name screen_name = sampleJSON["screen_name"] as NSString // Get status/entities/urls/[0]/url if let status :Dictionary = sampleJSON["status"] as? NSDictionary { if let entities :Dictionary = status["entities"] as? NSDictionary { if let urls :Array = entities["urls"] as? NSArray { if let urlSet :Dictionary = urls[0] as? NSDictionary { if let anUrl :String = urlSet["url"] as? NSString { url = anUrl } } } } }
書けました!
つ、つらい。。キャストつらいし、なんていうかつらい。*1 全てid型にぶっ込むObjective-Cのつらみとも言えます。*2
では、いわゆるObject
型ではなくてSwiftのDictionary
やArray
やString
やNumber
でJSONを持てば良いのでは?とも思うのですが、そうは問屋が卸しません。
SwiftではArray
もDictionary
も、宣言した型の要素しか入れることはできません。Array<String>
や、Dictionary<String, Number>
などです。
「なんでもid型だからプリミティブじゃなければ何でも突っ込んでいーよ」っていうObjective-bitchとは違うんです。*3
そんなつらみがあります。
解決方法
Swiftには優秀なEnumがあります。EnumでDictionary
やArray
やString
やNumber
をラップしてあげれば解決です。
SwiftyJSON
こんなかんじ
let sampleJSONData :NSData = NSData(contentsOfFile: "/path/to/sampleJSON.json") let sampleJSON :JSONValue = JSONValue(sampleJSONData) let screen_name :String? = sampleJSON["screen_name"].string let url :String? = sampleJSON["status"]["entities"]["urls"][0]["url"].string
簡潔な記述がよいかんじです。列挙型JSONValue
でラップし、.string
や.integer
などでアンラップします。
json-swift
こんなかんじ
let sampleJSONStringObj :String = NSString(contentsOfFile: "/path/to/sampleJSON.json") let sampleJSON :JSON? = JSON.parse(sampleJSONStringObj) var screen_name :String? var url :String? if let json :JSON = sampleJSON { screen_name = json["screen_name"]?.string url = json["status"]?["entities"]?["urls"]?[0]?["url"]?.string }
SwiftyJSONと大して変わりません。列挙型JSON
でラップし、.string
や.number
などでアンラップします。
違い
json-swiftの方はサンプルコードを見ての通り、?
を多用するスタイルです。subscript
(添字アクセス)がnil返すかもしれないってのがパッと見て分かるって利点はあるかもしれないですが、面倒くさいですね。
SwiftyJSONは、パースや添字アクセスには常にJSONValue
を返す(要素が無かったりしたらJSONValue.JInvalid
が入ります)ので、いちいち?
をつける必要がありません。
まとめ
Enumよい
*1:うおおおお、Objective-Cより簡潔で読みやすい!!!と思わなくもないけど
*2:気づいたら同じことがSwiftyJSONのREADMEに書いてありました。悲しい。
*3:僕はObjective-C好きですよ