Functional Programming in Swift
http://www.objc.io/books/
を読みながら、どうしてもEnumでJsonを表現するライブラリを作ってみたかったのです。
型セーフ、スレッドセーフで、案外いい感じです。
実際違ったアプローチで遊んでみたいという思いがこれを作らせました。
新しいビジョンが得られれば幸いです。
そこ、SwiftyJSONでいいとか言わない! まったくもう!
リポジトリはこちら
https://github.com/Ushio/EnumJson
※ また、Swift 1.2 なのでご注意ください。
※ Carthageに対応しています。
定義
public enum Json {
case JObject ([String : Json])
case JArray ([Json])
case JNumber (NSNumber)
case JString (String)
case JBoolean (Bool)
case JNull
}
とてもシンプルです。Dictionary,Arrayが一段挟まっているので再帰的定義に小細工は必要ありません。
要するにこれがやりたかっただけです。
生成
let json: Json = [
"string" : "string_value",
"number_double" : 10.5,
"number_int" : 15,
"boolean" : true,
"null" : nil,
"array" : ["red", "green", "blue"],
"object" : [
"one" : 1,
"two" : 2,
"three" : 3
]
]
もろもろConvertibleが用意されているので、直感的に記述できます。
書き出しや読み込み
if let data = json.jsonData {
// do something
}
if let json = EJson(data: data) {
// do something
}
アクセスショートカット
let json: Json = [
"string" : "string_value",
"number_double" : 10.5,
"number_int" : 15,
"boolean" : true,
"null" : nil,
"array" : ["red", "green", "blue"],
"object" : [
"one" : 1,
"two" : 2,
"three" : 3
]
]
let string_value: String? = json["string_value"]?.string
let green: String? = json["array" ~> 1]?.string
let three: Double? = json["object" ~> "three"]?.double
値のセット
subscriptで値をセットします。
var json: Json = [:]
json["string_key"] = "string"
json["key1" ~> "key2"] = true
json["key3"] = "a"
json["key3"] = "c"
let yes = json == [
"string_key" : "string",
"key1" : ["key2" : true],
"key3" : "c"
]
削除
nilをsubscriptで代入することで削除できます。
var json: Json = [
"string" : "string_value",
"number_double" : 10.5,
"number_int" : 15,
"array" : ["red", "green", "blue", "blue"],
"object" : [
"one" : 1,
"two" : 2,
"three" : 3
]
]
json["number_int"] = nil
json["array" ~> 1] = nil
json["object" ~> "one"] = nil
let yes = json == [
"string" : "string_value",
"number_double" : 10.5,
"array" : ["red", "blue", "blue"],
"object" : [
"two" : 2,
"three" : 3
]
]
オブジェクトへのマッピング
Swift 1.2ではifでシンプルに記述できるようになりました。
struct User {
let number: Double
let name: String
static func fromJson(json: Json) -> User? {
if
let number = json["number"]?.double,
let name = json["name"]?.string
{
return User(number: number, name: name)
}
return nil
}
}
let json: Json = [
"number" : 17.4,
"name" : "ken"
]
if let user = User.fromJson(json) {
// do something
}
※<*>演算子はEnumJsonから外すことにしました。ただ面白い手法なので、興味のある方はこちらの記事をお勧めします。
http://qiita.com/koher/items/95583f58a443f6a33c28
まとめ
単なる一発ネタだったのですが、組み立ててみると案外使えるものができたような気がしないでもないです(弱)
TODO リスト
- 名前空間(対応済み)
- asXxx => xxx (対応済み)
- Carthage対応 (対応済み)
- 数値精度問題対応 (不完全ではあるが、一部対応済み)
Carthage対応し、バージョン1.0を正式に決めました。
初期バージョンからは要素の削除や追加の方法がよりシンプルになるなど微調整が含まれています。