如何定义含可变字段的Swift Codable结构体?求data字段类型
处理Swift Codable中字段不固定的数组类型
根据你提供的JSON响应,data数组内的元素字段完全不固定,以下是几种适合的类型选择,可根据实际需求挑选:
方案一:固定值类型(所有value为字符串)
如果确定data元素的value都是字符串类型,直接使用[[String: String]]即可,无需额外解码逻辑:
struct ResponseData: Codable { let dataRes: [DataResStruct] struct DataResStruct: Codable { let data: [[String: String]] let key: String } }
这种方式最简洁,能直接通过JSONDecoder完成解析,适合仅需读取数据、无需类型安全校验的场景。
方案二:支持任意值类型
如果data元素的value可能包含字符串、数字、布尔等多种类型,可自定义一个AnyCodable类型来兼容所有Codable类型:
struct AnyCodable: Codable { let value: Any init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let int = try? container.decode(Int.self) { value = int } else if let string = try? container.decode(String.self) { value = string } else if let bool = try? container.decode(Bool.self) { value = bool } else if let double = try? container.decode(Double.self) { value = double } else if let array = try? container.decode([AnyCodable].self) { value = array.map { $0.value } } else if let dict = try? container.decode([String: AnyCodable].self) { value = dict.mapValues { $0.value } } else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "不支持的类型") } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case let int as Int: try container.encode(int) case let string as String: try container.encode(string) case let bool as Bool: try container.encode(bool) case let double as Double: try container.encode(double) case let array as [Any]: try container.encode(array.map(AnyCodable.init)) case let dict as [String: Any]: try container.encode(dict.mapValues(AnyCodable.init)) default: throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: container.codingPath, debugDescription: "不支持的类型")) } } }
使用时将data类型设为[[String: AnyCodable]]:
struct ResponseData: Codable { let dataRes: [DataResStruct] struct DataResStruct: Codable { let data: [[String: AnyCodable]] let key: String } }
这种方案能兼容任意结构的data元素,适合完全不确定响应结构的场景。
方案三:类型安全的枚举关联值
如果业务上可以通过外层的key字段区分data的结构(比如key="1"对应一种结构,key="2"对应另一种),可以用枚举关联值实现类型安全解析:
首先定义对应不同结构的模型:
struct DataItemType1: Codable { let key1_d: String let key2_d: String let key3_d: String } struct DataItemType2A: Codable { let key1_A: String let key2_A: String let key3_A: String let key4_A: String let key5_A: String } struct DataItemType2B: Codable { let key1_C: String }
然后定义枚举并自定义解码逻辑:
enum DataItem: Codable { case type1(DataItemType1) case type2A(DataItemType2A) case type2B(DataItemType2B) func encode(to encoder: Encoder) throws { switch self { case .type1(let item): try item.encode(to: encoder) case .type2A(let item): try item.encode(to: encoder) case .type2B(let item): try item.encode(to: encoder) } } } struct ResponseData: Codable { let dataRes: [DataResStruct] struct DataResStruct: Codable { let data: [DataItem] let key: String init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) key = try container.decode(String.self, forKey: .key) let rawData = try container.decode([[String: String]].self, forKey: .data) var items = [DataItem]() switch key { case "1": for rawItem in rawData { let itemData = try JSONSerialization.data(withJSONObject: rawItem) let item = try JSONDecoder().decode(DataItemType1.self, from: itemData) items.append(.type1(item)) } case "2": for rawItem in rawData { let itemData = try JSONSerialization.data(withJSONObject: rawItem) if let itemA = try? JSONDecoder().decode(DataItemType2A.self, from: itemData) { items.append(.type2A(itemA)) } else if let itemB = try? JSONDecoder().decode(DataItemType2B.self, from: itemData) { items.append(.type2B(itemB)) } else { throw DecodingError.dataCorruptedError(forKey: .data, in: container, debugDescription: "无法识别key=2对应的data元素类型") } } default: throw DecodingError.dataCorruptedError(forKey: .key, in: container, debugDescription: "未知的key值") } data = items } enum CodingKeys: String, CodingKey { case data, key } } }
这种方案能在解析时保证类型安全,后续处理data时可以通过枚举匹配做针对性逻辑,适合有明确业务区分的场景。
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

