解码子级时访问嵌套 Codable 结构中父结构的属性

Sin*_*hus 5 struct decoding swift codable identifiable

在嵌套Codable结构中使用解码器时,有没有办法访问父结构的属性?

我能想到的唯一方法(尚未测试)是在父结构中也使用手动解码器,在userInfo字典中设置属性,然后userInfo在子结构中访问。但这会导致大量样板代码。我希望有一个更简单的解决方案。

struct Item: Decodable, Identifiable {
    let id: String
    let title: String
    let images: Images

    struct Images: Decodable {
        struct Image: Decodable, Identifiable {
            let id: String
            let width: Int
            let height: Int

            init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                width = try container.decode(Int.self, forKey: .width)
                height = try container.decode(Int.self, forKey: .height)

                // How do I get `parent.parent.id` (`Item#id`) here?
                id = "\(parent.parent.id)\(width)\(height)"
            }
        }

        let original: Image
        let small: Image
        // …
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,来自服务器的项目 ID 仅在 JSON 的顶级属性中定义,但我也需要在子项中使用它们,因此我也可以使它们Identifiable.

Aod*_*odh 1

我使用 @New Dev 提到的 Itai Ferber 的建议进行了管理,方法如下:

\n
    \n
  1. 创建一个新的引用类型,其唯一目的是包含\n可以在父级和子级之间传递的可变值。
  2. \n
  3. 将该类型的实例分配给 JSONDecoder 的 userInfo 字典。
  4. \n
  5. 在解码父实例时检索该实例,并将您有兴趣传递的 id 分配给它。
  6. \n
  7. 在解码子级时,从先前存储在 userInfo 中的实例中检索该 id。
  8. \n
\n

我已将您上面的示例修改如下:

\n
struct Item: Decodable, Identifiable {\n\n    enum CodingKeys: String, CodingKey {\n        case id\n        case title\n        case images\n    }\n\n    let id: String\n    let title: String\n    let images: Images\n\n    struct Images: Decodable {\n        struct Image: Decodable, Identifiable {\n            let id: String\n            let width: Int\n            let height: Int\n\n            init(from decoder: Decoder) throws {\n                let container = try decoder.container(keyedBy: CodingKeys.self)\n                width = try container.decode(Int.self, forKey: .width)\n                height = try container.decode(Int.self, forKey: .height)\n\n                if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier {\n                    self.id = referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier\n                } else {\n                    self.id = "something went wrong"\n                }\n            }\n        }\n\n        let original: Image\n        let small: Image\n        // \xe2\x80\xa6\n\n        init(from decoder: Decoder) throws {\n            let container = try decoder.container(keyedBy: CodingKeys.self)\n            id = try container.decode(String.self, forKey: .id)\n            if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier {\n               referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier = id\n            }\n        }\n    }\n}\n\n// Use this reference type to just store an id that's retrieved later.\nclass ReferenceTypeUsedOnlyToContainAChangeableIdentifier {\n    var changeableIdentifier: String?\n}\n\n// Convenience extension.\nextension CodingUserInfoKey {\n    static let referenceTypeUsedOnlyToContainAChangeableIdentifier = CodingUserInfoKey(rawValue: "\\(ReferenceTypeUsedOnlyToContainAChangeableIdentifier.self)")!\n}\n\nlet decoder = JSONDecoder()\n// Assign the reference type here to be used later during the decoding process first to assign the id in `Item` and then\n// later to retrieve that value in `Images`\ndecoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] = ReferenceTypeUsedOnlyToContainAChangeableIdentifier()\n
Run Code Online (Sandbox Code Playgroud)\n