はじめに
Swiftで任意の形式のJSONを扱うには Swiftで面倒なJSONの取り扱いをさらに10倍便利にするclass JSON がかなり便利です。
しかし、大抵のAPIなどで得られるJSONは既に構造が決まっていて、そのやりとりやその後の処理を記述する部分では、可能ならば型安全の恩恵を受けたいと思うことがあります。
Json Schema というのはそういう方向性なのかもしれませんが、無駄に大きいというか、現状ちょっと扱いづらい。
ということで、↓こういうオレオレコードジェネレータを作ったのでご紹介します。
https://github.com/mokemokechicken/ObjectJsonMapperGenerator
使い方
次のStepです。
- JSONの構造を定義する
- それからSwiftコードを自動生成する
- 使う
JSONの構造を定義する
JSONの構造をYAMLで定義します。
例えば、こんな感じです。
Book:
authors: [Author]
title: String
year: Int
note?: String
price: Double
option?:
hoge?: String
hara?: Bool
Author:
name: String
others?: [Book]
- Entityを定義していきます。定義したEntityも構造に含むことができます。
- そのKeyがOptionの時は Key名の後ろに ? を付けます。Swift風ですが、この書き方は簡潔で気に入っています。
- Arrayは Swift的に
[型]
で表します。 - ただ、 ArrayのArrayは作れません。
- 何故か?実装がややこしくて… 使わないだろうしドンマイです。
- どうしてもというときは、ダミーのEntityでも間に挟むということで…
それからSwiftコードを自動生成する
上記のProjectをCloneして、 bin/make_ojm.rb
を実行します。こんな感じです。
git clone https://github.com/mokemokechicken/ObjectJsonMapperGenerator.git
cd ObjectJsonMapperGenerator
ruby bin/make_ojm.rb -l swift -c book.yml > Book.swift
この Book.swift に 上記の構造のJSONをシリアライズ・デシリアライズするコードが生成されます。
ちなみに、現時点では↓こんなコードになります。
https://gist.github.com/mokemokechicken/2fae3969dcfaeb09aa18
使う
こんな感じに使います。
let data = NSData(contentsOfFile: "path/to/json")!
if let book = Book.fromData(data) { // 読み込み
println("Title: \(book.title), Price: $\(book.price)")
println(book.toJsonString()) // JSONにする
} else {
// 定義された構造として読み込みできなかった
println("JSON Parse Error")
}
今回の仕様として、定義された構造としてJSONをParseできない場合は、nil を返します。
Swiftには例外機構がない(ないですよね?)ので、Parseの深い部分でエラーがあるときに、そこから延々とnilをChainして戻していく必要がありました。なので生成されたコードは結構野暮ったいです。
ん〜もっと良い書き方は無かったのかな。。。
余談
- 日付や時刻型欲しい! という場合は、 extensionなどで拡張してもらう方向で考えています。
- Validation とかも extension で記述していく方向で良いのかな。
- EntityのBaseとなるClassにNSObjectを継承させるかは迷っています。
- 簡単な動作検証用Xcode Project → https://github.com/mokemokechicken/ObjectJsonMappingSwiftTest
さいごに
この続きでAPI通信する部分も自動生成したいな、と思っています。
Java版もいつか作りたいですが、Swift版がある程度落ち着いてからかな。