Swift中JSONDecoder解析API数据出现keyNotFound错误解决问询
解决JSONDecoder keyNotFound错误、null值处理及ViewModel属性访问问题
1. 修复keyNotFound错误
问题根源
去掉decoder.keyDecodingStrategy = .convertFromSnakeCase后,解码器会严格匹配结构体属性名与JSON中的key(比如JSON里的FIRST_NAME和结构体的firstName完全不匹配),直接触发keyNotFound错误。
两种可行修复方案
方案一:手动配置CodingKeys映射
直接在结构体里声明CodingKeys,明确对应JSON的key:
struct Person: Codable { var firstName: String var midName: String? // 先标记为可选类型,处理null值 var lastName: String enum CodingKeys: String, CodingKey { case firstName = "FIRST_NAME" case midName = "MID_NAME" case lastName = "LAST_NAME" } } struct CustomerResponse: Codable { var customers: [Person] // 如果JSON顶级key是其他命名,比如"CUSTOMERS",同样要加CodingKeys映射 }
方案二:重新启用蛇形转驼峰策略
如果JSON的key是蛇形命名(如FIRST_NAME),启用.convertFromSnakeCase后,解码器会自动将全大写蛇形key转为驼峰属性名,只需保证结构体属性名是对应驼峰格式:
struct Person: Codable { var firstName: String var midName: String? var lastName: String } struct CustomerResponse: Codable { var customers: [Person] } // 解码时重新启用策略 let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase
2. 处理JSON中的null值
JSON中可能为null的字段(比如Vanessa的MID_NAME),必须将结构体对应属性声明为可选类型,解码器遇到null时会自动赋值为nil,避免崩溃:
// 错误写法:无法解码null,会崩溃 // var midName: String // 正确写法:接收null值 var midName: String?
如果需要给null字段设置默认值(比如空字符串),可以在结构体里加计算属性:
struct Person: Codable { var firstName: String var midName: String? var lastName: String // 带默认值的安全访问属性 var safeMidName: String { midName ?? "" } }
3. 实现ViewModel访问Person属性
创建CustomerListViewModel,内部持有Person数组,封装属性访问逻辑,方便UI层调用:
class CustomerListViewModel { private var customers: [Person] = [] // 初始化时传入API解码得到的客户数组 init(customers: [Person]) { self.customers = customers } // 获取客户总数 var customerCount: Int { customers.count } // 根据索引获取单个客户(或直接返回属性) func customer(at index: Int) -> Person? { guard index >= 0 && index < customers.count else { return nil } return customers[index] } // 封装单个属性的访问方法,避免UI层直接依赖Person结构体 func firstName(for index: Int) -> String? { customer(at: index)?.firstName } func safeMidName(for index: Int) -> String { customer(at: index)?.safeMidName ?? "" } }
在Webservice中整合解码与ViewModel初始化
class Webservice { func fetchCustomers(completion: @escaping (Result<CustomerListViewModel, Error>) -> Void) { guard let url = URL(string: "你的API地址") else { completion(.failure(NSError(domain: "无效URL", code: -1, userInfo: nil))) return } URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { completion(.failure(error)) return } guard let data = data else { completion(.failure(NSError(domain: "未获取到数据", code: -2, userInfo: nil))) return } do { let decoder = JSONDecoder() // 根据你选择的方案,启用蛇形转驼峰或使用CodingKeys decoder.keyDecodingStrategy = .convertFromSnakeCase let response = try decoder.decode(CustomerResponse.self, from: data) let viewModel = CustomerListViewModel(customers: response.customers) completion(.success(viewModel)) } catch { completion(.failure(error)) print("解码错误:\(error)") } }.resume() } }
内容的提问来源于stack exchange,提问作者texi
相关产品推荐
相关产品推荐

