如何简单处理Codable属性的两种数据类型解码(空值为数组)?
解决Codable解码时同字段多类型(字典/空数组)的问题
这种JSON格式不一致的坑我可太熟了!后端有时候为了省事,空值返回空数组而不是null,直接搞崩Codable的自动解码。给你两个简单直接的解决办法,都是基于自定义解码逻辑实现的:
方法一:直接在父结构体里做容错解码
这是最省事的方案,直接在包含clinic_info的父结构体里自定义init(from decoder:)方法,先尝试正常解码,失败后处理空数组的情况:
// 先定义正常的ClinicInfo结构体 struct ClinicInfo: Codable { let city: String? let state: String? let country: String? let phone: String? let ext: String? let coords: String? let location: String? } // 父API响应结构体 struct APIResponse: Codable { let clinicInfo: ClinicInfo? // 映射JSON字段名 enum CodingKeys: String, CodingKey { case clinicInfo = "clinic_info" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 第一步:尝试按正常字典类型解码 do { self.clinicInfo = try container.decode(ClinicInfo.self, forKey: .clinicInfo) } catch { // 解码失败(说明是数组),直接解码空数组并忽略 _ = try? container.decode([String].self, forKey: .clinicInfo) // 把clinicInfo设为nil,也可以根据需求返回空的ClinicInfo实例 self.clinicInfo = nil } } }
逻辑说明
核心就是「先试正常路径,失败就处理异常」:
- 优先尝试把
clinic_info解码成我们需要的ClinicInfo结构体 - 遇到类型不匹配错误(即后端返回空数组)时,顺手解码掉空数组(这里用
[String]只是因为空数组对元素类型不敏感,解码任意数组类型都能成功) - 最后把
clinicInfo设为nil,你也可以改成返回一个默认的空ClinicInfo实例
方法二:用枚举封装多类型(更优雅可扩展)
如果觉得直接写do-catch不够清晰,可以用枚举封装两种可能的类型,逻辑更直观,也方便后续扩展:
struct ClinicInfo: Codable { let city: String? let state: String? let country: String? let phone: String? let ext: String? let coords: String? let location: String? } // 枚举封装两种可能的类型:有效信息/空数组 enum ClinicInfoContainer: Codable { case info(ClinicInfo) case emptyArray init(from decoder: Decoder) throws { // 尝试解码成ClinicInfo if let validInfo = try? decoder.singleValueContainer().decode(ClinicInfo.self) { self = .info(validInfo) } // 尝试解码成空数组 else if let emptyArray = try? decoder.singleValueContainer().decode([Any].self), emptyArray.isEmpty { self = .emptyArray } // 其他情况抛出错误 else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "Expected ClinicInfo dictionary or empty array for clinic_info" ) ) } } } // 父响应结构体 struct APIResponse: Codable { let clinicInfo: ClinicInfo? enum CodingKeys: String, CodingKey { case clinicInfo = "clinic_info" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let containerValue = try container.decode(ClinicInfoContainer.self, forKey: .clinicInfo) // 把枚举值转换成我们需要的ClinicInfo?类型 switch containerValue { case .info(let info): self.clinicInfo = info case .emptyArray: self.clinicInfo = nil } } }
逻辑说明
用枚举明确定义字段的两种合法状态,解码时逐一匹配:
- 先尝试解码成有效
ClinicInfo实例 - 失败后检查是不是空数组
- 其他非法格式(比如非空数组)直接抛出错误,避免无效数据流入
小提示
如果后端偶尔会返回非空的数组(虽然你说明是空数组),可以在处理数组的逻辑里加个判断,确认数组为空才设为nil,否则抛出错误,避免把无效数据当成空值处理。
内容的提问来源于stack exchange,提问作者Zerhogie
相关产品推荐
相关产品推荐

