Swift 自定义Decodable解码:将不同zone的blocks映射为对应类型数组
自定义Decodable实现JSON结构转业务模型
核心实现思路:解码时不直接把zones解析为固定结构的数组,而是先读取每个zone的name标识,动态决定blocks字段的解码类型,最终将同类型的block聚合到Root结构对应的独立数组属性中,全程保持类型安全,无需依赖无类型的Any/字典中转。
实现代码
首先定义内部使用的辅助类型,用于承接zones数组的原始结构:
// 匹配接口返回的zone名称标识 private enum ZoneIdentifier: String, Decodable { case zoneA, zoneB, zoneC, zoneD } // zones数组单个元素的临时解码结构 private struct RawZone: Decodable { let name: ZoneIdentifier let blockContainer: KeyedDecodingContainer<RawZone.CodingKeys> enum CodingKeys: String, CodingKey { case name, blocks } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(ZoneIdentifier.self, forKey: .name) // 持有blocks字段的解码容器,后续按需解析对应类型 blockContainer = container } }
给Root实现Decodable协议:
extension Root: Decodable { private enum RootCodingKeys: String, CodingKey { case zones } init(from decoder: Decoder) throws { // 初始化临时变量承接各类型的元素数组 var elementsA: [ElementA] = [] var elementsB: [ElementB] = [] var elementsC: [ElementC] = [] var elementsD: [ElementD] = [] let rootContainer = try decoder.container(keyedBy: RootCodingKeys.self) // 解码得到所有原始zone数据 let rawZones = try rootContainer.decode([RawZone].self, forKey: .zones) // 遍历每个zone,按name匹配对应类型解码blocks for rawZone in rawZones { switch rawZone.name { case .zoneA: elementsA = try rawZone.blockContainer.decode([ElementA].self, forKey: .blocks) case .zoneB: elementsB = try rawZone.blockContainer.decode([ElementB].self, forKey: .blocks) case .zoneC: elementsC = try rawZone.blockContainer.decode([ElementC].self, forKey: .blocks) case .zoneD: elementsD = try rawZone.blockContainer.decode([ElementD].self, forKey: .blocks) } } // 给结构体常量属性赋值 self.elementsA = elementsA self.elementsB = elementsB self.elementsC = elementsC self.elementsD = elementsD } }
适配说明
- 只要
ElementA/ElementB/ElementC/ElementD本身遵守Decodable协议,不需要额外实现自定义解码逻辑 - 如果接口可能返回未定义的zone名称,在switch语句中增加
default: break分支即可忽略未知zone,不会中断整体解码 - 如果需要做容错处理(比如单个zone解析失败不影响整体返回),可以给单zone的解码逻辑包裹
do-catch块自行处理错误 - 该实现完全兼容Decodable原生的错误传递机制,字段缺失、类型不匹配等错误会正常抛出
内容的提问来源于stack exchange,提问作者superpuccio
相关产品推荐
相关产品推荐

