How does the Swift 4 Decodable protocol cope with a dictionary containing a key whose name is not known until runtime? For example:

  [
    {
      "categoryName": "Trending",
      "Trending": [
        {
          "category": "Trending",
          "trailerPrice": "",
          "isFavourit": null,
          "isWatchlist": null
        }
      ]
    },
    {
      "categoryName": "Comedy",
      "Comedy": [
        {
          "category": "Comedy",
          "trailerPrice": "",
          "isFavourit": null,
          "isWatchlist": null
        }
      ]
    }
  ]

Here we have an array of dictionaries; the first has keys categoryName and Trending, while the second has keys categoryName and Comedy. The value of the categoryName key tells me the name of the second key. How do I express that using Decodable?

推荐答案

关键在于如何定义CodingKeys属性.虽然它通常是enum,但它可以是符合CodingKey协议的任何东西.要制作动态键,可以调用静态函数:

struct Category: Decodable {
    struct Detail: Decodable {
        var category: String
        var trailerPrice: String
        var isFavorite: Bool?
        var isWatchlist: Bool?
    }

    var name: String
    var detail: Detail

    private struct CodingKeys: CodingKey {
        var intValue: Int?
        var stringValue: String

        init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
        init?(stringValue: String) { self.stringValue = stringValue }

        static let name = CodingKeys.make(key: "categoryName")
        static func make(key: String) -> CodingKeys {
            return CodingKeys(stringValue: key)!
        }
    }

    init(from coder: Decoder) throws {
        let container = try coder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.detail = try container.decode([Detail].self, forKey: .make(key: name)).first!
    }
}

Usage:

let jsonData = """
  [
    {
      "categoryName": "Trending",
      "Trending": [
        {
          "category": "Trending",
          "trailerPrice": "",
          "isFavourite": null,
          "isWatchlist": null
        }
      ]
    },
    {
      "categoryName": "Comedy",
      "Comedy": [
        {
          "category": "Comedy",
          "trailerPrice": "",
          "isFavourite": null,
          "isWatchlist": null
        }
      ]
    }
  ]
""".data(using: .utf8)!

let categories = try! JSONDecoder().decode([Category].self, from: jsonData)

(I changed isFavourit in the JSON to isFavourite since I thought it was a mispelling. It's easy enough to adapt the code if that's not the case)

Json相关问答推荐

JOLT转换,将属性复制到同级别的dict中

将json数组反序列化为选项 struct

如何形成正确的JQ表达式以从JSON文件中获得准确的输出数据?

如何用JQ更改空/布尔/数字的 colored颜色 ?

从Postgres表中的JSON中提取值

如何使用jq按键 Select 并获取整个json输出来更改json中的多个值

如何在 terraform 输出中打印一组用户信息

是否可以在有条件的情况下将 json 对象转换为 JOLT 中的数组?

如何解决名为 null 的map值

将 YAML 文件转换为 Python JSON 对象

了解 JSON Schema 草稿版本 4 中的additionalProperties关键字

如何使用 Jackson 重命名 JSON 序列化中的根键

如何在 json 编码字符串内的子数组数据周围添加方括号?

如何使用 Newtonsoft.Json 包在 C#(4.0) 中解析我的 json 字符串?

在 React 中访问子级的父级状态

JSON日期到Java日期?

PHP json_encode json_decode UTF-8

python追加到json对象中的数组

如何在dart Flutter 中将json字符串转换为json对象?

如何从 github API 解析链接头