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

Swift解码keyNotFound(hours)错误排查及Place结构体优化咨询

Swift解码错误排查与Place结构体简化方案

解码错误(keyNotFound: "hours")排查与解决

出现这个致命错误的核心原因是:解码时找不到hours键,但你的结构体里把它定义成了非可选的必填字段,再加上用了try!强制解包,直接触发崩溃。结合你说数据库已有hours列,具体排查和解决方向如下:

  • 先确认实际返回的数据:数据库有列不代表接口/数据源返回的JSON里一定包含hours键,可能是部分数据行的hours为空导致接口没返回该字段,或者字段名拼写不一致(比如数据库是opening_hours,但接口没映射成hours)。
  • 把hours设为可选类型:如果允许Place实例没有营业时间数据,直接修改字段定义为可选,这样解码时缺失键只会赋值为nil,不会报错:
struct Place: Codable {
    let admission: String
    let website: String
    let hours: String? // 改为可选类型
}
  • 给非可选字段加默认值:如果hours必须有值,但数据源可能缺失,自定义解码逻辑赋默认值,不用手动处理每个字段:
struct Place: Codable {
    let admission: String
    let website: String
    let hours: String
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        admission = try container.decode(String.self, forKey: .admission)
        website = try container.decode(String.self, forKey: .website)
        // 缺失hours时赋默认值
        hours = try container.decodeIfPresent(String.self, forKey: .hours) ?? "暂无营业时间"
    }
}
  • 替换try!为安全处理:try!会把所有解码错误直接转为致命错误,改用do-catch或try?避免崩溃:
do {
    let place = try JSONDecoder().decode(Place.self, from: data)
    // 处理数据
} catch {
    print("解码失败:\(error)")
}

简化Place结构体写法,避免重复定义CodingKeys

Swift的Codable协议本身支持自动合成CodingKeys,大部分场景下不需要手动写:

  • 字段名与键名完全一致时:直接让结构体遵循Codable即可,自动匹配字段和键名,无需任何额外代码:
struct Place: Codable {
    let admission: String
    let website: String
    let hours: String?
}
  • 字段名与键名不一致时:只需要在CodingKeys里定义不匹配的字段,其余字段自动合成:
struct Place: Codable {
    let admission: String
    let website: String
    let openingHours: String? // 结构体字段名
    
    enum CodingKeys: String, CodingKey {
        case admission, website
        case openingHours = "hours" // 仅映射不一致的键
    }
}
  • 批量处理默认值:如果多个字段需要默认值,用@propertyWrapper简化,避免重复写解码逻辑:
@propertyWrapper
struct Default<T: Codable> {
    var wrappedValue: T
    let defaultValue: T
    
    init(defaultValue: T) {
        self.defaultValue = defaultValue
        self.wrappedValue = defaultValue
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decodeIfPresent(T.self) ?? defaultValue
    }
}

extension Default: Encodable where T: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(wrappedValue)
    }
}

// 使用时直接给字段加注解
struct Place: Codable {
    let admission: String
    let website: String
    @Default(defaultValue: "暂无营业时间") var hours: String
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 02:01:50