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

Posted

技术标签:

【中文标题】解码子时嵌套 Codable 结构中父结构的访问属性【英文标题】:Access property of parent struct in a nested Codable struct when decoding the child 【发布时间】:2020-09-26 06:07:49 【问题描述】:

在嵌套的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
        // …
    

在上面的示例中,来自服务器的项目 ID 仅在 JSON 中的***属性中定义,但我在子项中也需要它们,因此我也可以将它们设为 Identifiable

【问题讨论】:

@jawadAli 不是。 您的userInfo 想法几乎可以肯定是正确的方法。问题是它会生成什么样的样板,我们可以帮助消除它。 (我过去曾构建过这样的系统,标准的重构技术适用于为其删除代码重复。) Codable 的首席工程师 ItaiFerber 在此讨论了多种方法:forums.swift.org/t/codable-passing-data-to-child-decoder/12757/… 【参考方案1】:

我使用 @New Dev 提到的 Itai Ferber 的建议通过以下方式管理它:

    创建一个新的引用类型,其唯一目的是包含一个 可以在父子之间传递的可变值。 将该类型的实例分配给 JSONDecoder 的 userInfo 字典。 在解码父实例时检索该实例,并为其分配您有兴趣传递的 id。 在解码子节点时,从之前存储在 userInfo 中的实例中检索该 ID。

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

struct Item: Decodable, Identifiable 

    enum CodingKeys: String, CodingKey 
        case id
        case title
        case images
    

    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)

                if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier 
                    self.id = referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier
                 else 
                    self.id = "something went wrong"
                
            
        

        let original: Image
        let small: Image
        // …

        init(from decoder: Decoder) throws 
            let container = try decoder.container(keyedBy: CodingKeys.self)
            id = try container.decode(String.self, forKey: .id)
            if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier 
               referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier = id
            
        
    


// Use this reference type to just store an id that's retrieved later.
class ReferenceTypeUsedOnlyToContainAChangeableIdentifier 
    var changeableIdentifier: String?


// Convenience extension.
extension CodingUserInfoKey 
    static let referenceTypeUsedOnlyToContainAChangeableIdentifier = CodingUserInfoKey(rawValue: "\(ReferenceTypeUsedOnlyToContainAChangeableIdentifier.self)")!


let decoder = JSONDecoder()
// Assign the reference type here to be used later during the decoding process first to assign the id in `Item` and then
// later to retrieve that value in `Images`
decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] = ReferenceTypeUsedOnlyToContainAChangeableIdentifier()

【讨论】:

以上是关于解码子时嵌套 Codable 结构中父结构的访问属性的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Codable 结构中捕获 init(来自解码器:解码器)中的错误?

如何处理常见的JSON嵌套结构

在 swift 中对动态键和动态对象使用 Codable

如何使用 Codable 解码具有更改键的 json 响应?

Swift 使用Codable协议进行json转模型

获取自定义类型的Date()