You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift中Codable解析JSON出现keyNotFound(HasError)错误求助

排查JSONDecoder解码失败的问题

咱们来一步步拆解你遇到的keyNotFound错误,其实背后藏着几个Codable模型和JSON结构不匹配的问题:

错误原因分析

  • HasError类型不匹配:原始JSON里HasError是字符串值"FALSE",但你在WebResponse里把它定义成了Bool类型——JSONDecoder没法自动把字符串"FALSE"转成Bool,这会直接导致解码失败。
  • ResultData的CodingKeys完全错误:你的ResultData结构体里,CodingKeys把resultData映射到"ResultData",但原始JSON里ResultData下面直接是"C"字段,这个映射逻辑完全不对,解码器找不到对应的键。
  • CData里的字段名不匹配CData里的types字段映射的是"Types",但原始JSON里对应的是"Type",少了个后缀"s",导致解码器找不到该字段。
  • 遗漏了C下的U字段:原始JSON里C对象还有一个"U"子对象,你的CData里没定义这个字段,默认情况下JSONDecoder遇到未知键会报错(虽然当前报错是HasError,但这个问题也得一起解决才能正常解码)。

解决方案

咱们逐个修正这些问题:

1. 修正HasError的类型处理

WebResponse里的HasError改成String类型,再通过计算属性转成Bool值,这样既兼容JSON的字符串格式,又能在代码里方便使用Bool逻辑:

struct WebResponse: Codable {
    var hasError: String
    var responseMessage: String
    var result: String
    var resultData: ResultData
    
    // 计算属性,把字符串转成Bool
    var isError: Bool {
        return hasError.uppercased() == "TRUE"
    }
    
    enum CodingKeys: String, CodingKey {
        case hasError = "HasError"
        case responseMessage = "ResponseMessage"
        case result = "Result"
        case resultData = "ResultData"
    }
}

2. 修正ResultData的结构

ResultData应该直接包含C对象,而不是错误地映射到ResultData本身:

struct ResultData: Codable {
    var c: CData
    
    enum CodingKeys: String, CodingKey {
        case c = "C"
    }
}

3. 修正CData的字段名并补充U字段

types改成type(对应JSON里的"Type"),同时补充U的子结构体和字段:

// 新增U字段对应的结构体
struct UData: Codable {
    var emailVerified: String
    
    enum CodingKeys: String, CodingKey {
        case emailVerified = "EmailVerified"
    }
}

struct CData: Codable {
    var corporateId: String
    var customerId: String
    var customerName: String
    var gcmid: String
    var loyaltyPoint: String
    var oldGCMID: String
    var type: String
    var userName: String
    var u: UData? // 用可选类型,防止万一接口返回时没有这个字段
    
    enum CodingKeys: String, CodingKey {
        case corporateId = "CorporateId"
        case customerId = "CustomerId"
        case customerName = "CustomerName"
        case gcmid = "GCMID"
        case loyaltyPoint = "LoyaltyPoint"
        case oldGCMID = "OldGCMID"
        case type = "Type"
        case userName = "UserName"
        case u = "U"
    }
}

4. 解码时允许忽略未知键(可选)

如果接口可能返回未定义的字段,可以给JSONDecoder设置忽略未知键,避免不必要的报错:

let decoder = JSONDecoder()
decoder.unknownKeysPolicy = .ignore // 忽略未知键
let jsonRes = try decoder.decode(WebResponse.self, from: dataFromString)
print(jsonRes)

修正后的完整测试代码

import Foundation

struct UData: Codable {
    var emailVerified: String
    
    enum CodingKeys: String, CodingKey {
        case emailVerified = "EmailVerified"
    }
}

struct CData: Codable {
    var corporateId: String
    var customerId: String
    var customerName: String
    var gcmid: String
    var loyaltyPoint: String
    var oldGCMID: String
    var type: String
    var userName: String
    var u: UData?
    
    enum CodingKeys: String, CodingKey {
        case corporateId = "CorporateId"
        case customerId = "CustomerId"
        case customerName = "CustomerName"
        case gcmid = "GCMID"
        case loyaltyPoint = "LoyaltyPoint"
        case oldGCMID = "OldGCMID"
        case type = "Type"
        case userName = "UserName"
        case u = "U"
    }
}

struct ResultData: Codable {
    var c: CData
    
    enum CodingKeys: String, CodingKey {
        case c = "C"
    }
}

struct WebResponse: Codable {
    var hasError: String
    var responseMessage: String
    var result: String
    var resultData: ResultData
    
    var isError: Bool {
        hasError.uppercased() == "TRUE"
    }
    
    enum CodingKeys: String, CodingKey {
        case hasError = "HasError"
        case responseMessage = "ResponseMessage"
        case result = "Result"
        case resultData = "ResultData"
    }
}

// 测试解码逻辑
let jsonString = """
{
 "WebResponse": {
 "HasError": "FALSE",
 "ResponseMessage": "Signed in Successfully.",
 "Result": "SUCCESS",
 "ResultData": {
 "C": {
 "CustomerId": "656",
 "CustomerName": "swift son",
 "UserName": "swift@infinx.com",
 "Type": "FORM",
 "OldGCMID": "eA5RV0TSCoY:APA91bFea67cIlKlVes6Scdgth4CiHSzpk78hE89953-l6gK4O9xflYAs0p24jzccBtqQgAMoRaEM4SOdJ1546QDtYk2nm64cZurwz58c9Pmww4JLRDA7wtWSktpdFdVkX94VoiSCPdm",
 "GCMID": "",
 "LoyaltyPoint": "0",
 "CorporateId": "19",
 "U": {
 "EmailVerified": "1"
 }
 }
 }
 }
}
"""

if let data = jsonString.data(using: .utf8) {
    do {
        let decoder = JSONDecoder()
        decoder.unknownKeysPolicy = .ignore
        let response = try decoder.decode(WebResponse.self, from: data)
        print("解码成功:\(response)")
        print("是否有错误:\(response.isError)")
    } catch {
        print("解码失败:\(error)")
    }
}

这样修改后,就能成功解码你的JSON数据了。

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

火山引擎 最新活动