Swift中基于公共字段动态解码多类型Decodable对象的方案
Swift 基于响应类型动态选择Codable结构体解码
针对你需要根据resultType字段动态解码不同Codable结构体的场景,以下是两种简洁的实现方案:
方案1:使用枚举作为联合类型(推荐)
定义一个包含所有目标类型关联值的枚举,通过自定义解码逻辑先读取resultType,再匹配对应的结构体完成解码。这种方式类型安全,编译器会强制覆盖所有case,避免遗漏。
// 定义各类型的Result结构体 struct Result1: Codable { let resultType: String = "result1" let field1: String // 该类型独有字段 } struct Result2: Codable { let resultType: String = "result2" let field2: Int // 该类型独有字段 } // 定义联合枚举,作为所有Result类型的统一容器 enum ResultUnion: Codable { case result1(Result1) case result2(Result2) // 新增类型时只需添加对应case // 自定义解码逻辑 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let resultType = try container.decode(String.self, forKey: .resultType) switch resultType { case "result1": let result = try Result1(from: decoder) self = .result1(result) case "result2": let result = try Result2(from: decoder) self = .result2(result) default: throw DecodingError.dataCorruptedError( forKey: .resultType, in: container, debugDescription: "未知的resultType: \(resultType)" ) } } // 自定义编码(若不需要编码可省略) func encode(to encoder: Encoder) throws { switch self { case .result1(let result): try result.encode(to: encoder) case .result2(let result): try result.encode(to: encoder) } } private enum CodingKeys: String, CodingKey { case resultType } } // 使用示例 do { let decodedResult = try JSONDecoder().decode(ResultUnion.self, from: responseData) switch decodedResult { case .result1(let res): // 处理Result1逻辑 print(res.field1) case .result2(let res): // 处理Result2逻辑 print(res.field2) } } catch { print("解码失败:\(error)") }
方案2:基于公共协议的可扩展解码
如果需要支持大量Result类型,可通过定义公共协议来统一识别类型,避免重复编写解码逻辑,新增类型时只需遵循协议即可。
// 定义公共协议,要求类型提供对应的resultType标识 protocol ResultIdentifiable: Codable { static var resultTypeKey: String { get } } // 让各Result结构体遵循协议 struct Result1: ResultIdentifiable { let resultType: String = "result1" let field1: String static var resultTypeKey: String { "result1" } } struct Result2: ResultIdentifiable { let resultType: String = "result2" let field2: Int static var resultTypeKey: String { "result2" } } // 解码工具类 enum DynamicResultDecoder { static func decode(from data: Data, supportedTypes: [ResultIdentifiable.Type]) throws -> ResultIdentifiable { // 临时解码仅获取resultType字段 struct TempResult: Codable { let resultType: String } let temp = try JSONDecoder().decode(TempResult.self, from: data) // 匹配对应的目标类型 guard let targetType = supportedTypes.first(where: { $0.resultTypeKey == temp.resultType }) else { throw DecodingError.dataCorruptedError( forKey: TempResult.CodingKeys.resultType, in: try data.keyedContainer(), debugDescription: "不支持的resultType: \(temp.resultType)" ) } return try JSONDecoder().decode(targetType, from: data) } private static extension Data { func keyedContainer() throws -> KeyedDecodingContainer<TempResult.CodingKeys> { try JSONDecoder().container(keyedBy: TempResult.CodingKeys.self) } } } // 使用示例 do { let supportedTypes: [ResultIdentifiable.Type] = [Result1.self, Result2.self] let result = try DynamicResultDecoder.decode(from: responseData, supportedTypes: supportedTypes) switch result { case let res as Result1: print(res.field1) case let res as Result2: print(res.field2) default: break } } catch { print("解码失败:\(error)") }
方案对比
- 枚举方案:类型安全,switch分支会被编译器检查,适合类型数量较少、固定的场景。
- 协议方案:扩展性强,新增类型无需修改解码核心逻辑,适合类型较多或需要动态扩展的场景。
内容的提问来源于stack exchange,提问作者Avba
相关产品推荐
相关产品推荐

