如何使用Codable解码含同名键的嵌套JSON?
解决Codable解码嵌套JSON时同名键的冲突问题
嗨,这个问题我之前做API对接时也碰到过,核心问题就是你在同一个CodingKey枚举里重复使用了"message"作为raw value——Swift的枚举要求每个case的raw value必须唯一,所以编译器才会报错。咱们一步步来解决它:
问题根源拆解
你的后端返回逻辑是互斥的:
- 成功时:返回顶层
message,无error字段 - 失败时:返回嵌套在
error里的message,无顶层message
而你原来的代码试图在同一个loginResponseKeys枚举里,让success_message和error_message都映射到"message",这就违反了CodingKey的唯一性要求。
解决方案1:拆分CodingKey枚举(最直观)
我们为顶层字段和嵌套的error字段分别定义独立的CodingKey枚举,这样就不会有raw value冲突的问题了:
// 遵循Swift命名规范:结构体名大写开头,变量名驼峰式 struct LoginResponse: Codable { let success: Bool let successMessage: String? let errorMessage: String? // 顶层字段的CodingKey enum TopLevelKeys: String, CodingKey { case success case message case error } // Error嵌套对象的CodingKey enum ErrorKeys: String, CodingKey { case message } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: TopLevelKeys.self) // 先解码必传的success字段 success = try container.decode(Bool.self, forKey: .success) // 根据success的值分支处理(贴合后端互斥逻辑) if success { // 成功场景:解码顶层message successMessage = try container.decode(String.self, forKey: .message) errorMessage = nil } else { // 失败场景:解码嵌套error里的message successMessage = nil let errorContainer = try container.nestedContainer(keyedBy: ErrorKeys.self, forKey: .error) errorMessage = try errorContainer.decode(String.self, forKey: .message) } } }
解决方案2:拆分Error结构体(更优雅)
把嵌套的error单独封装成一个结构体,让它自己处理内部的message字段,这样顶层和嵌套的message属于不同结构体的属性,自然不会冲突:
struct APIError: Codable { let message: String } struct LoginResponse: Codable { let success: Bool private let message: String? private let error: APIError? // 对外提供统一的消息获取入口 var displayMessage: String { message ?? error?.message ?? "Unknown error" } enum CodingKeys: String, CodingKey { case success, message, error } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) success = try container.decode(Bool.self, forKey: .success) // 同样根据success分支处理 if success { message = try container.decode(String.self, forKey: .message) error = nil } else { message = nil error = try container.decode(APIError.self, forKey: .error) } } } // 使用示例 let response = try JSONDecoder().decode(LoginResponse.self, from: jsonData) print(response.displayMessage) // 直接拿到成功/失败的消息
额外优化建议
- 遵循Swift命名规范:结构体名、枚举名首字母大写,变量名用驼峰式(比如
LoginResponse而非loginResponse,successMessage而非success_message) - 利用后端返回的互斥逻辑(成功时无error,失败时无顶层message),可以避免使用
decodeIfPresent,让解码逻辑更严谨
内容的提问来源于stack exchange,提问作者user165242
相关产品推荐
相关产品推荐

