LoginSignup
122
118

More than 5 years have passed since last update.

[Swift] ObjectMapper で JSON データのマッピング

Last updated at Posted at 2016-02-29

こんにちは。

なんつうかもう、気付いたらそこら中 JSON ばっかりですね。サーバから JSON のデータを持ってきてパースしてる人は世界中にどんだけいるんでしょーか。

取得した JSON は、データ構造に応じて適切にモデルクラスに落とし込みたいですよね。ですよね? Swift でどうやるか調べたところ、ObjectMapper を使うとそんなこんながとても簡単に実現できました。こいつはなかなか便利な奴です。

Alamofire と組み合わせたり Realm と組み合わせたりもできて素敵です。今回は書いてませんが、JSON のキャッシュとして Realm と一緒に使ってます。あんまり意識せずにすんなり使えるのがいいですね。

セットアップ

CocoaPods や Carthage でインストールできますので Readme をご参考に

基本的な使い方

たとえば、ユーザデータが入った JSON があるとして。

[
    { "name": "takayama" },
    { "name": "takahashi" },
    { "name": "yamada" }
]

これに対応するクラスを作って。

class User: Mappable {
    var name: String?

    required init?(_ map: Map) {
    }

    func mapping(map: Map) {
        name <- map["name"]
    }
}

こんな風に読み込む。

let users: [ User ]? = Mapper<User>().mapArray(jsonString)
print(users?[0].name) // => Optional("takayama")

たとえば JSON の中に name がなかった場合は user.name の値は nil になりますけど、var name: String = "" とかやっておけば空文字になりますし、var name: String = "Default" とかだと特定の値をデフォルト値にできます。

まあ基本的な使い方はオリジナルのドキュメント見ればわかります。ここまでは簡単です。

型変換

例えば JSON のデータ方が文字列型だった場合に数値型に変換してくれるような仕組みやらがあるのですが、この辺がなかなか理解できなくて苦労しました。特に難しかったのが NSDate への変換です。

Custom Transforms という仕組みがあって、ドキュメントを読むと DateTransform というのを使って NSDate に対応しているように見えます。ただこれ、データソースが UnixTime を想定しているんですよね。1456727500 的な。我々のデータは yyy-MM-dd HH:mm:ss なのでこのままでは無理ゲー。

class User: Mappable {
    var birthday: NSDate?

    required init?(_ map: Map) {

    }

    func mapping(map: Map) {
        birthday <- (map["birthday"], DateTransform())
    }
}

Custom Transforms まわりのソースを参照するとサブクラスがいくつかあって、その中の CustomDateFormatTransform というやつがそのものずばりで使えました。TransformType クラスを継承すれば自作もできそうです。

そんでもって CustomDateFormatTransform を使うとこんな簡単に…!

class User: Mappable {
    var birthday: NSDate?

    required init?(_ map: Map) {

    }

    func mapping(map: Map) {
        birthday <- (map["birthday"], CustomDateFormatTransform(formatString: "yyyy-MM-dd HH:mm:ss"))
    }
}

他に URLTransform を使うと NSURL にできます。

var url: NSURL?

func mapping(map: Map) {
    url <- (map["url"], URLTransform())
}

EnumTransform というのもあります。

enum DataType: Int {
    case Unknown, Work, Home
}

...

var type: DataType?

func mapping(map: Map) {
    type <- (map["type"], EnumTransform<DataType>())
}

全部入り

今回紹介したやつで作るとこんな感じでしょうか。

class User: Mappable {
    enum DataType: Int {
        case Unknown, Work, Home
    }

    var name: String?
    var birthday: NSDate?
    var type: DataType = .Unknown
    var url: NSURL?

    required init?(_ map: Map) {
    }

    func mapping(map: Map) {
        name     <- map["name"]
        birthday <- (map["birthday"], CustomDateFormatTransform(formatString: "yyyy-MM-dd HH:mm:ss"))
        type     <- (map["type"], EnumTransform<DataType>())
        url      <- (map["url"], URLTransform())
    }
}

JSON

[
{
    "name": "takayama",
    "birthday": "1999-09-09 00:00:00",
    "type": 2,
    "url": "http://twitter.com/takayama"
},
{
    "name": "takahashi",
    "birthday": "1999-01-01 00:00:00",
    "type": 1,
    "url": "http://www.example.com/"
},
{
    "name": "yamada",
    "birthday": "2016-02-29 15:20:00",
    "type": 2,
    "url": "http://qiita.com/takayama"
}
]

実行してみる。

let users: [ User ]? = Mapper<User>().mapArray(jsonString)

for user in users! {
   print(user.name, user.birthday, user.type.rawValue, user.url)
}

出力結果。

Optional("takayama") Optional(1999-09-08 15:00:00 +0000) 2 Optional(http://twitter.com/takayama)
Optional("takahashi") Optional(1998-12-31 15:00:00 +0000) 1 Optional(http://www.example.com/)
Optional("yamada") Optional(2016-02-29 06:20:00 +0000) 2 Optional(http://qiita.com/takayama)
122
118
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
122
118