You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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) // 直接拿到成功/失败的消息

额外优化建议

  1. 遵循Swift命名规范:结构体名、枚举名首字母大写,变量名用驼峰式(比如LoginResponse而非loginResponse,successMessage而非success_message)
  2. 利用后端返回的互斥逻辑(成功时无error,失败时无顶层message),可以避免使用decodeIfPresent,让解码逻辑更严谨

内容的提问来源于stack exchange,提问作者user165242

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:39:05