解码子时嵌套 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(来自解码器:解码器)中的错误?