如何从Codable响应模型提取name字段的FirstKey值或字符串内容
问题解决:Codable处理多格式name字段并提取值
问题背景
服务器返回的JSON中,name字段存在两种格式:
- 格式1:包含
First数组的对象数组,示例:[{"First": ["DSR"]}] - 格式2:直接字符串,示例:
"Deepak Singh Rawat"
已通过NameUnion枚举实现解码,但需要提取对应的值(First数组中的内容或直接字符串)。
现有代码问题
读取响应代码存在两处错误:
- 重复定义
abModel变量 - 使用未定义的
response变量
解决方案
1. 优化模型,添加计算属性直接获取姓名
修改NameUnion枚举,新增计算属性displayName,自动处理两种格式的取值逻辑:
enum NameUnion: Codable { case nameElementArray([NameElement]) case string(String) // 新增计算属性,直接返回要显示的姓名 var displayName: String { switch self { case .nameElementArray(let elements): // 取第一个元素的firstKey数组的第一个值,为空则返回空字符串 return elements.first?.firstKey?.first ?? "" case .string(let name): return name } } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let x = try? container.decode([NameElement].self) { self = .nameElementArray(x) return } if let x = try? container.decode(String.self) { self = .string(x) return } throw DecodingError.typeMismatch(NameUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for NameUnion")) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .nameElementArray(let x): try container.encode(x) case .string(let x): try container.encode(x) } } }
2. 修正读取响应代码
修复变量定义错误,并演示如何遍历提取每个元素的姓名:
do { if let path = Bundle.main.path(forResource: "jsonfilename", ofType: "json") { let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped) let nameModel = try JSONDecoder().decode(NameModel.self, from: data) // 遍历所有元素,提取姓名 for element in nameModel { if let nameUnion = element.name { print("提取到的姓名:\(nameUnion.displayName)") } } } } catch { print("解码失败:\(error.localizedDescription)") }
3. 可选优化:简化NameElement取值逻辑
如果NameElement中First数组只会有一个元素,可以修改firstKey的类型为String?,简化后续取值:
struct NameElement: Codable { var firstKey: String? enum CodingKeys: String, CodingKey { case firstKey = "First" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) // 直接取数组第一个元素 firstKey = try values.decodeIfPresent([String].self, forKey: .firstKey)?.first } }
对应displayName的逻辑可简化为:
var displayName: String { switch self { case .nameElementArray(let elements): return elements.first?.firstKey ?? "" case .string(let name): return name } }
效果验证
运行修正后的代码,会输出:
提取到的姓名:DSR 提取到的姓名:Deepak Singh Rawat
内容的提问来源于stack exchange,提问作者DSRawat
相关产品推荐
相关产品推荐

