subscriptを活用してSwiftyにXMLやJSONのパスを記述する

# イントロ
Swift製JSONパーサーとしてよく使わせるライブラリの一つに[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)があります。
このライブラリでは、JSONオブジェクトに対して以下の様なパスの書き方ができます。

```swift
let jsonStr = "{ \"animal\": { \"element\": [{\"name\": \"cat\"}, {\"name\": \"dog\"}] } }"
let json = JSON.parse(jsonStr)

// パスのデータにアクセスする
let name = json["animal", "element", 1, "name"].string // -> "dog"
```
目的データまでのパスを、配列からデータを取り出すような形で指定しています。これは、Swiftが持つ[Subscripts](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html)という仕組みを上手く活用することで実現されます。どのようにすればできるのかライブラリのソースコードを基にまとめてみました。
# 自分で作ったクラスにSubscriptsを行う
Swiftには**subscript**という予約語があり、角括弧を自作クラスの中で使って配列のようなアクセスが実現できます。

- [Swift の Subscript について](http://qiita.com/happy_ryo/items/72b68859ed8ace9f5fb4)

引数には任意の型を指定できます。例えばDictionaryのように文字列をキーとしたオブジェクトの返却などもできます。

```swift

class SomeClass {
    subscript(key key: String) -> SomeClass {
     ...
    }

    subscript(index index) -> SomeClass {
     ...
    }
}

```

SwiftyJSONではJSONというクラスの中に文字列や数値に対するsubscriptを実装しています。
そのため、角括弧でJSONのキーやIndex番号にアクセスが可能です。

```swift
let jsonStr = "{ \"animal\": { \"element\": [{\"name\": \"cat\"}, {\"name\": \"dog\"}] } }"
let json = JSON.parse(jsonStr)

let subJSON = json["animal"]["element"][1]
```

具体的な実装箇所はSwiftJSON.swiftのL470-L489の箇所です。StringとIntを引数として受け取るsubscriptが実装されています。
https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift#L446L489

```swift:SwiftyJSON.swift
extension JSON {
// ...
// (省略)
// ...
    private subscript(index index: Int) -> JSON {
     // ...
    }

    // 引数が文字列でJSONオブジェクトを返す。
    private subscript(key key: String) -> JSON {
     // ...
    }
// ...
// (省略)
// ...
}
```

ただし**privateメソッド**として作られていて、外からアクセスできるようにはなっていません。実際にはSwiftyJSON.swiftのL520から始まる以下の2つのsubscriptを使ってアクセスします。次の節からこの２つについて説明します。

```swift:SwiftyJSON.swift
public subscript(path: [JSONSubscriptType]) -> JSON
public subscript(path: JSONSubscriptType...) -> JSON
```

https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift#L520L556


# 配列や可変長引数を受け取れるようにする
**Subscripts**が任意の型を受け取れるということは、配列を渡すこともできます。

```swift
class SomeClass {
    subscript(path: [String]) -> SomeClass {
     ...
    }
}
```

また、Swiftは引数の末尾へ**...**をつけて可変長引数を渡せるようにできます。

```swift
class SomeClass {
    subscript(path: String...) -> SomeClass {
     ...
    }
}
```

そうすると、以下のように角括弧を使って複数の文字列を渡すことができます。

```swift
let obj = SomeClass()

// Array
let path = ["hoge", "fuga"]
let data1 = obj[path]

// Variadic
let data2 = obj["hoge", "fuga"]
```

SwiftyJSONはL520-L557でその**subscript**を記述しています。

https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift#L520L557

```swift:SwiftyJSON.swift
extension JSON {
// ...
// (省略)
// ...
    public subscript(path: [JSONSubscriptType]) -> JSON {
     // ... 
    }

    public subscript(path: JSONSubscriptType...) -> JSON {
     // ...
    }
// ...
// (省略)
// ...
}

```

では、**JSONSubscriptType**というのはなんでしょうか。

# Protocolを使って複数の型を取れるようにする
配列や可変長引数を使うことで、subscriptの引数に複数の値を組み合わせて渡せるようになりました。ただし、その場合に渡せる型は１種類に限定されます。JSONの場合、配列とハッシュを組み合わせてデータ構造を構成するため、ハッシュのキーと配列のIndex番号を一緒にして渡すことができれば便利です。そのために、SwiftyJSONでは**JSONSubscriptType**というProtocolを作り、それに従っている型はsubscriptへ渡せるようにしています。L427-L441を読むと、Int・Stringがそれによって拡張されています。

https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift#L427L441

```swift:SwiftyJSON.swift

// subscriptに指定できるProtocolを定義
public protocol JSONSubscriptType {
    var jsonKey:JSONKey { get }
}

extension Int: JSONSubscriptType {
 ...
}

extension String: JSONSubscriptType {
 ...
}
```

こうすることで、IntとStringをカンマで繋いで、パス指定をすることが可能になります。

```swift
let jsonStr = "{ \"animal\": { \"element\": [{\"name\": \"cat\"}, {\"name\": \"dog\"}] } }"
let json = JSON.parse(jsonStr)

// Array
let path: [JSONSubscriptType] = ["animal", "element", 1, "name"]
let name = json[path].string // -> "dog"

// Variadic
let name = json["animal", "element", 1, "name"].string // -> "dog"
```


# まとめ
以上のようにSwiftyJSONはsubscriptの性質をうまく利用することにより、ArrayやDictinaryを扱うようにJSONデータにアクセスできるようにしています。
ポイントをまとめると次のようになります。

- subscriptキーワードでJSONデータに"[]"でのアクセスを可能にする
- subscriptへ可変長引数を渡せるようにして、パス指定を実現
- 引数の型をProtocolにすることで、StringとIntの両方をまとめて渡せるようにする

subscriptの使い方に関しては、こちらの記事がさらに網羅的にまとめているので合わせて参照してください。

- [あなたは何個分かる? Swiftのsubscriptの使い方7選](http://qiita.com/shimesaba/items/2ab64d1ff97f5cde80cb)

また、以前仕事でこれらの仕組みを使ったXMLパーサーを開発しました。OSSとしても公開されているので、もしよろしければご利用ください。

- [yahoojapan/SwiftyXMLParser](https://github.com/yahoojapan/SwiftyXMLParser)