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

Swift Codable解码JSON遇缺失键仍报错,已用Optional无效如何解决

Swift Codable 缺失键场景解码失败问题解决方案

核心问题定位

你当前解码失败的根本原因是类型不匹配,并非缺失键导致。你提供的JSON样例中,dynamic_obj数组第一条对象的latitude、longitude字段为Double数值类型,但你的Dynamic_obj结构体将这两个属性声明为String类型,解码时类型不匹配抛出错误。

你当前使用的decodeIfPresent本身就是Codable处理缺失键的标准方案,只要字段类型匹配,缺失对应键时会自动赋值为nil或者你设置的默认值,不会触发解码失败。

修复方案

方案1:属性类型改为Double,按需转字符串(推荐)

修改Dynamic_obj的属性声明和解码逻辑即可:

struct Dynamic_obj : Codable {
    var dt : String?
    var url : String?
    let desc : String?
    var flag : Int?
    let count : String?
    let field : String?
    let visible : Bool?
    
    var bankname : String = "NA"
    var pdfPassword : String = "NA"
    // 修改属性类型为Double可选
    var latitude : Double?
    var longitude : Double?
    var address : String = "NA"

    // CodingKeys可简化,无需重复赋值相同名称的键
    enum CodingKeys: String, CodingKey {
        case dt, url, desc, flag, count, field, visible
        case bankname, pdfPassword, latitude, longitude, address
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dt = try values.decodeIfPresent(String.self, forKey: .dt)
        url = try values.decodeIfPresent(String.self, forKey: .url)
        desc = try values.decodeIfPresent(String.self, forKey: .desc)
        flag = try values.decodeIfPresent(Int.self, forKey: .flag)
        count = try values.decodeIfPresent(String.self, forKey: .count)
        field = try values.decodeIfPresent(String.self, forKey: .field)
        visible = try values.decodeIfPresent(Bool.self, forKey: .visible)
        
        bankname = try values.decodeIfPresent(String.self, forKey: .bankname) ?? "NA"
        pdfPassword = try values.decodeIfPresent(String.self, forKey: .pdfPassword) ?? "NA"
        // 改解码类型为Double,缺失则赋值nil
        latitude = try values.decodeIfPresent(Double.self, forKey: .latitude)
        longitude = try values.decodeIfPresent(Double.self, forKey: .longitude)
        address = try values.decodeIfPresent(String.self, forKey: .address) ?? "NA"
    }
    
    // 若需要字符串格式的经纬度,可新增计算属性
    var latitudeStr: String {
        guard let lat = latitude else { return "NA" }
        return "\(lat)"
    }
    
    var longitudeStr: String {
        guard let lon = longitude else { return "NA" }
        return "\(lon)"
    }
}

方案2:保持属性为String类型,解码时做类型兼容

如果业务要求必须把经纬度存为String类型,可在解码时增加类型判断自动转换:

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    // 其他属性解码逻辑保持不变
    // ...
    
    // 兼容数值类型的经纬度,自动转字符串
    if let latDouble = try values.decodeIfPresent(Double.self, forKey: .latitude) {
        latitude = "\(latDouble)"
    } else {
        latitude = try values.decodeIfPresent(String.self, forKey: .latitude) ?? "NA"
    }
    
    if let lonDouble = try values.decodeIfPresent(Double.self, forKey: .longitude) {
        longitude = "\(lonDouble)"
    } else {
        longitude = try values.decodeIfPresent(String.self, forKey: .longitude) ?? "NA"
    }
}

调试优化建议

后续遇到解码错误时,可在catch块打印完整错误信息,能直接定位到具体出错的字段和原因:

catch {
    print("解码错误:\(error)")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 18:36:03