Swift中自定义Decoder初始化:从JSON动态键获取Option的id值
自定义Decodable初始化实现动态id字段解析
需求说明
需要为指定结构体实现自定义Decodable初始化逻辑,适配JSON中Option的id字段动态变化的场景:
- 若
OptionContainer包含randomKey字段,则用该字段的值作为键,提取对应值作为Option.id - 若
randomKey不存在,则直接使用Option的id字段
原结构体代码
struct OptionContainer: Decodable { let randomKey: String? let id: String let text: String struct Option: Decodable { let id: String let text: String } }
目标JSON数据
[ { "id": "123", "text": "text", "randomKey": "level", "options": [ { "text": "Hello", "level": "1" }, { "text": "Hello2", "level": "2" }, { "text": "Hello3", "level": "3" } ] }, { "id": "222", "text": "text2", "options": [ { "text": "Hello", "id": "1" }, { "text": "Hello2", "id": "2" }, { "text": "Hello3", "id": "3" } ] } ]
完整实现代码
首先为OptionContainer补充options属性,同时为Option实现带动态id字段名参数的自定义初始化:
struct OptionContainer: Decodable { let randomKey: String? let id: String let text: String let options: [Option] struct Option: Decodable { let id: String let text: String // 接收动态id字段名的自定义初始化方法 init(from decoder: Decoder, idKey: String) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.text = try container.decode(String.self, forKey: .text) // 尝试读取动态指定的id字段,失败则fallback到默认id字段 if let dynamicId = try? container.decode(String.self, forKey: CodingKeys(stringValue: idKey)!) { self.id = dynamicId } else { self.id = try container.decode(String.self, forKey: .id) } } enum CodingKeys: String, CodingKey { case id, text } } // OptionContainer的自定义初始化 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(String.self, forKey: .id) self.text = try container.decode(String.self, forKey: .text) self.randomKey = try container.decodeIfPresent(String.self, forKey: .randomKey) // 确定Option要使用的id字段名 let targetIdKey = self.randomKey ?? "id" // 解析options数组,为每个Option传入目标id字段名 var optionsContainer = try container.nestedUnkeyedContainer(forKey: .options) var parsedOptions = [Option]() while !optionsContainer.isAtEnd { let optionDecoder = try optionsContainer.superDecoder() let option = try Option(from: optionDecoder, idKey: targetIdKey) parsedOptions.append(option) } self.options = parsedOptions } enum CodingKeys: String, CodingKey { case id, text, randomKey, options } }
关键逻辑说明
- Option的动态解析:新增带
idKey参数的初始化方法,优先按传入的字段名读取id值;若动态字段不存在,则使用默认的id字段(可根据需求调整错误处理逻辑,比如直接抛出解码错误)。 - OptionContainer的统筹解析:先解析自身基础字段确定目标id键名,再遍历options数组的解码器,为每个Option传入对应键名完成动态解析。
验证测试
使用JSONDecoder解析目标JSON,验证结果:
let jsonData = """ [ { "id": "123", "text": "text", "randomKey": "level", "options": [ { "text": "Hello", "level": "1" }, { "text": "Hello2", "level": "2" }, { "text": "Hello3", "level": "3" } ] }, { "id": "222", "text": "text2", "options": [ { "text": "Hello", "id": "1" }, { "text": "Hello2", "id": "2" }, { "text": "Hello3", "id": "3" } ] } ] """.data(using: .utf8)! do { let containers = try JSONDecoder().decode([OptionContainer].self, from: jsonData) containers.forEach { container in print("容器ID: \(container.id)") print("选项ID列表: \(container.options.map { $0.id })") } } catch { print("解码错误: \(error)") }
输出结果:
容器ID: 123 选项ID列表: ["1", "2", "3"] 容器ID: 222 选项ID列表: ["1", "2", "3"]
内容的提问来源于stack exchange,提问作者fewqfeqfewfqwf
相关产品推荐
相关产品推荐

