LoginSignup
10
6

More than 5 years have passed since last update.

Swift4 CodableでJsonデータの値をネストさせてエンコードする

Posted at

Swift4からのCodableのエンコード処理少し迷ったところがあったのでメモ。

一対一形式のJSONをEncodableに対応

下記のようなKeyとValueが一対一になっているJSONがあるとします。
ログインユーザーを表すAPIのリクエストデータを想定しています。


{
  "id": 0,
  "first_name": "tommy",
  "user_token": "AAAAAA"
}

上記のJSONをCodableでエンコードすると以下のようになります。

struct User: Encodable {
    var id: Int
    var firstName: String
    var userToken: String
    enum CodingKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case userToken = "user_token"
    }
}

let user = try! JSONEncoder().encode(User(id: 0, firstName: "tommy", userToken: "AAAAAA"))
print(String(data: user, encoding: String.Encoding.utf8)!)

CodingKeysでJSONキーを指定すれば、エンコードができます。
便利。

Jsonの構造が変わった場合

困ったのがこんな場合の時。
サーバー側の仕様が変わって先程のJSONに「userというトップのキーを追加する」仕様が加わったとします。

{
  "user": {
    "id": 0,
    "first_name": "tommy",
    "user_token": "AAAAAA"
  }
}

この場合にSwiftコードはどうするべきか。

Encodableに適応している型であればエンコードができるので新しい型を作るのがまず考えられます。
RootUser型を新しく作ってみます。

struct RootUser: Encodable {
    var user: User
}

let user3 = try! JSONEncoder().encode(RootUser(user:User(id: 0, firstName: "tommy", userToken: "AAAAAA")))
print(String(data: user3, encoding: String.Encoding.utf8)!)

これでも期待するJSONデータを作ることができます。
が、Jsonの形式が変わる度に新しい型を作るのはちょっと面倒。

マニュアルエンコードでネストさせる

JSONの形式がSwiftの型と一致しない場合独自にエンコード実装をすることができます。
こうすればJSON形式が変わってもその度にSwiftの方では型を新しく追加する必要はありません。


struct User: Encodable {
    var id: Int
    var firstName: String
    var userToken: String
    enum CodingKeys: String, CodingKey {
        case user
    }
    private enum NestedKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case userToken = "user_token"
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        var nestObject = container.nestedContainer(keyedBy: NestedKeys.self, forKey: .user)
        try nestObject.encode(id, forKey: .id)
        try nestObject.encode(firstName, forKey: .firstName)
        try nestObject.encode(userToken, forKey: .userToken)
    }
}

let user = try! JSONEncoder().encode(User(id: 0, firstName: "tommy", userToken: "AAAAAA"))
print(String(data: user, encoding: String.Encoding.utf8)!)

CodingKeysuserというキーを指定して、さらにネストされた各キーをNestedKeysで指定します。

func encode(to encoder:)を実装することで独自にエンコードすることができます。
encoder.containerでトップのコンテナを取得した後に、container.nestedContainerメソッドを実行することで、ネストされたObjectを取得できます。
あとはencodeメソッドで各プロパティをエンコードすればよいです。

結果

{
  "user": {
    "id": 0,
    "first_name": "tommy",
    "user_token": "AAAAAA"
  }
}

まとめ

独自でEncodableでエンコードする方法を紹介しました。
Encodableが自動で行ってくれるのもいいのですが、今回のように「ネストされたデータ形式は今まで同じだから型を新しく作るまででもない」っていう時は上手くハマると思います。

10
6
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
10
6