Help us understand the problem. What is going on with this article?

Swiftで面倒なJSONの取り扱いをさらに10倍便利にするclass JSON

More than 3 years have passed since last update.

Swiftで面倒なJSONの取り扱いを10倍便利にするSwiftyJSONをもってしても、まだJavaScriptよりも面倒だったのが悔しかったので書きました。

Synopsis

AnyObjectなSwiftオブジェクトも、こうして JSON Stringにできます。

let obj:[String:AnyObject] = [
    "array": [JSON.null, false, 0, "",[],[:]],
    "object":[
        "null":   JSON.null,
        "bool":   true,
        "int":    42,
        "double": 3.141592653589793,
        "string": "a α\t\n?",
        "array":  [],
        "object": [:]
    ],
    "url":"http://blog.livedoor.com/dankogai/"
]

let json = JSON(obj)
json.toString()
// "{\"array\":[null,false,0,\"\",[],{}],
//  \"object\":{\"int\":42,\"double\":3.141592653589793,
//  \"string\":\"a α\t弾\n?\",\"object\":{},\"null\":null,
//  \"bool\":true,\"array\":[]},
//  \"url\":\"http://blog.livedoor.com/dankogai/\"}"

もちろん文字列からも

let json = JSON.parse("{\"array\":[...}")

URLからも。

let json = JSON.fromURL("http://api.dan.co.jp/jsonenv")

JSONの歩き方。

添字で葉までいって、型をあわせるだけ。

json["object"]["null"].asNull       // NSNull()
json["object"]["bool"].asBool       // true
json["object"]["int"].asInt         // 42
json["object"]["double"].asDouble   // 3.141592653589793
json["object"]["string"].asString   // "a α\t弾\n?"

json["array"][0].asNull             // NSNull()
json["array"][1].asBool             // false
json["array"][2].asInt              // 0
json["array"][3].asString           // ""

SwiftyJSONと同様、存在しない添字にアクセスしても爆発しません。その代わり発生したエラーをほいほいと次のメソッドに投げていきます。Optional ChainならぬNSError Chainといったところでしょうか。

if let b = json["noexistent"][1234567890]["entry"].asBool {
    // ....
} else {
    let e = json["noexistent"][1234567890]["entry"].asError
    println(e)
} // Error Domain=JSONErrorDomain Code=404 "["noexistent"] not found" UserInfo=0x10064bfc0 {NSLocalizedDescription=["noexistent"] not found}

ここまではSwiftyJSONと同様なのですが、一つ困ったことがあったのです。

SwiftyJSONのJSONValueは、enumだったのです。

なんでenumだと困るのでしょう?

class JSONは継承可

ここでJavaScriptにおけるJSONの取り扱いを見てみましょう。

//json["object"]["string"] vs...
  json.object.string

はい、さらに短い。オブジェクトの一分岐ごとに4文字も。Swift(というか厳密にはNSDictionary)では添字を使わざるを得なかったところを、JavaScriptではプロパティで行けるからです。

でもプロパティの追加は、何もJavaScriptの専売特許ではありませんよね?

こうすればいけるではありませんか?

//// schema by subclassing
class MyJSON : JSON {
    override init(_ obj:AnyObject){ super.init(obj) }
    override init(_ json:JSON)  { super.init(json) }
    var null  :NSNull? { return self["null"].asNull }
    var bool  :Bool?   { return self["bool"].asBool }
    var int   :Int?    { return self["int"].asInt }
    var double:Double? { return self["double"].asDouble }
    var string:String? { return self["string"].asString }
    var url:   String? { return self["url"].asString }
    var array :MyJSON  { return MyJSON(self["array"])  }
    var object:MyJSON  { return MyJSON(self["object"]) }
}

行けました!

let myjson = MyJSON(obj)
myjson.object.null      // NSNull?
myjson.object.bool      // Bool?
myjson.object.int       // Int?
myjson.object.double    // Double?
myjson.object.string    // String?
myjson.url              // String?

JavaScriptではゼロ円でやってくれる連想配列のプロパティ化のために、一行メソッドの羅列とはいえclassを一個定義するのはめんどくさくも感じます。その一方、ソースコードできちんと定義しておくことじゃ、意図しないプロパティをtypoで作ってしまうことがありえないことも意味します。そもそもコンパイル通りませんし、IDEによる補完もこれで効くようになります。さらに各プロパティ(というかgetter)で入力検証したりするのもこれで自由自在です。

Swiftにはプリミティブでない型が三種類もあります。が、classには他の二つと二つの大きな違いがあります。一つは値の代入で、残りの二つが値渡しなのに対して参照渡しであること。そしてもう一つが、継承をサポートしていることです。

structenumにも、新たなプロパティを追加するのはextensionで可能です。が、これだとオールオアナッシングなんですよね。サイトごとにschemaを変えたいってのが出来ない。

この問題があるので、ArrayDictionaryにもclass版が欲しいところではありますね。

Dan the Man with too Much Data to (De)?serialize

追記[2016.01.29]: Swift 2にも対応しているのみならず、Playgroundも付いています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした