12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift の JSONDecoder で keyDecodingStrategy を使う時の注意点

Posted at

まとめ

  • JSONDecoder.keyDecodingStrategyconvertFromSnakeCase の場合、 Decodable を適用した struct で CodingKeys を指定する際は key 名の指定に気をつける
  • key 名はすでに camelCase に変換した状態で指定する

JSONDecoder と Decodable

Swift で JSON を struct に変換できる JSONDecoder 、便利ですよね。

let decoder = JSONDecoder()
try decoder.decode(Response.self, from: data)

などとして、JSON 形式であるはずの DatadataResponse 型に変換できます。

基本的に構造体のプロパティ名と JSON の Key 名が揃っているならば、 Response 型が単に Decodable を適用していればなんの問題もありません。

let jsonData = """
{
  "id": 123,
  "screenName": "mokumoku"
}
""".data(using: .utf8)!

struct Response: Decodable {
  let id: Int
  let screenName: String
}

let decoder = JSONDecoder()
try decoder.decode(Response.self, from: jsonData)  // 成功

CodingKeys

しかしそう上手くいくことは少なく、 Swift は lowerCamelCase 文化であるのに対し API の key は snake_case で設定されていることが多いです。
そのような時は Response 型に CodingKeys を設定します。

let jsonData = """
{
  "id": 123,
  "screen_name": "mokumoku"
}
""".data(using: .utf8)!


struct Response: Decodable {
  let id: Int
  let screenName: String

  private enum CodingKeys: String, CodingKey {
    case id
    case screenName = "screen_name"
  }
}

let decoder = JSONDecoder()
try decoder.decode(Response.self, from: jsonData)  // 成功

これだとどんな key 名にも対応できてよいのですが、例えばもっとたくさんプロパティがあって、一つだけ snake_case な場合は面倒です。
CodingKeys を実装する場合はすべてのプロパティを case に書き出さないとエラーになるので単純に面倒なのと実装漏れの恐れがあります。

KeyDecodingStorategy

そんな場合に便利なのが KeyDecodingStorategy です。

  • convertFromSnakeCase
  • useDefaultKeys
  • custom(@escaping ([CodingKey]) -> CodingKey)

の三種類があります。

ここで convertFromSnakeCase を指定すると、自動的に JSON の key 名の snake_case を lowerCamelCase に変換してくれます。

let jsonData = """
{
  "id": 123,
  "screen_name": "mokumoku"
}
""".data(using: .utf8)!


struct Response: Decodable {
  let id: Int
  let screenName: String
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase  // ここが大事
try decoder.decode(Response.self, from: jsonData)  // CodingKeys がなくても成功

便利ですね。
しかし、ここで JSON の key 名が微妙なために CodingKeys を実装しようとするとハマるポイントがあったので紹介します。(前フリが長すぎる)

KeyDecodingStrategy と CodingKeys 併用時の注意点

Twitter の API レスポンス はそのサービスの特性上、 idUInt64 に収まらないものがあるため使い物になりません。
ではどうするかというと、 id_str という key に String で正しい値が返ってきているのでそれを利用します。

Swift 側では id: String として、これに id_str を対応させるようにします。

これをそのままやると以下のようになりますが、decode に失敗します。


let jsonData = """
{
  "id": 123,
  "id_str": "123",
  "screen_name": "mokumoku"
}
""".data(using: .utf8)!


struct Response: Decodable {
    let id: String
    let screenName: String

    private enum CodingKeys: String, CodingKey {
        case id = "id_str"
        case screenName
    }
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
try decoder.decode(Response.self, from: jsonData)  // "id_str" という key がないですというエラー

ではどうするかというと、enumrawValue を指定するところを lowerCamelCase にしてから指定します。


private enum CodingKeys: String, CodingKey {
  case id = "idStr"
  case screenName
}

どうやら JSON の key 名を keyDecodingStrategy に従って変換してから、struct の CodingKeys にマッチさせているようで、ちょっと気持ち悪いのですがこうすると上手くいきました。

まとめ

  • keyDecodingStrategyCodingKeys を併用する場合は key 名の指定に気をつけよう

間違っていたり、もっとベストプラクティスがあるかもしれないのでもしご存知ならばコメントなどで教えていただけるとありがたいです。

リンク

12
9
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
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?