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

如何简单处理Codable属性的两种数据类型解码(空值为数组)?

解决Codable解码时同字段多类型(字典/空数组)的问题

这种JSON格式不一致的坑我可太熟了!后端有时候为了省事,空值返回空数组而不是null,直接搞崩Codable的自动解码。给你两个简单直接的解决办法,都是基于自定义解码逻辑实现的:

方法一:直接在父结构体里做容错解码

这是最省事的方案,直接在包含clinic_info的父结构体里自定义init(from decoder:)方法,先尝试正常解码,失败后处理空数组的情况:

// 先定义正常的ClinicInfo结构体
struct ClinicInfo: Codable {
    let city: String?
    let state: String?
    let country: String?
    let phone: String?
    let ext: String?
    let coords: String?
    let location: String?
}

// 父API响应结构体
struct APIResponse: Codable {
    let clinicInfo: ClinicInfo?
    
    // 映射JSON字段名
    enum CodingKeys: String, CodingKey {
        case clinicInfo = "clinic_info"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 第一步:尝试按正常字典类型解码
        do {
            self.clinicInfo = try container.decode(ClinicInfo.self, forKey: .clinicInfo)
        } catch {
            // 解码失败(说明是数组),直接解码空数组并忽略
            _ = try? container.decode([String].self, forKey: .clinicInfo)
            // 把clinicInfo设为nil,也可以根据需求返回空的ClinicInfo实例
            self.clinicInfo = nil
        }
    }
}

逻辑说明

核心就是「先试正常路径,失败就处理异常」:

  • 优先尝试把clinic_info解码成我们需要的ClinicInfo结构体
  • 遇到类型不匹配错误(即后端返回空数组)时,顺手解码掉空数组(这里用[String]只是因为空数组对元素类型不敏感,解码任意数组类型都能成功)
  • 最后把clinicInfo设为nil,你也可以改成返回一个默认的空ClinicInfo实例

方法二:用枚举封装多类型(更优雅可扩展)

如果觉得直接写do-catch不够清晰,可以用枚举封装两种可能的类型,逻辑更直观,也方便后续扩展:

struct ClinicInfo: Codable {
    let city: String?
    let state: String?
    let country: String?
    let phone: String?
    let ext: String?
    let coords: String?
    let location: String?
}

// 枚举封装两种可能的类型:有效信息/空数组
enum ClinicInfoContainer: Codable {
    case info(ClinicInfo)
    case emptyArray
    
    init(from decoder: Decoder) throws {
        // 尝试解码成ClinicInfo
        if let validInfo = try? decoder.singleValueContainer().decode(ClinicInfo.self) {
            self = .info(validInfo)
        } 
        // 尝试解码成空数组
        else if let emptyArray = try? decoder.singleValueContainer().decode([Any].self), emptyArray.isEmpty {
            self = .emptyArray
        } 
        // 其他情况抛出错误
        else {
            throw DecodingError.dataCorrupted(
                DecodingError.Context(
                    codingPath: decoder.codingPath,
                    debugDescription: "Expected ClinicInfo dictionary or empty array for clinic_info"
                )
            )
        }
    }
}

// 父响应结构体
struct APIResponse: Codable {
    let clinicInfo: ClinicInfo?
    
    enum CodingKeys: String, CodingKey {
        case clinicInfo = "clinic_info"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let containerValue = try container.decode(ClinicInfoContainer.self, forKey: .clinicInfo)
        
        // 把枚举值转换成我们需要的ClinicInfo?类型
        switch containerValue {
        case .info(let info):
            self.clinicInfo = info
        case .emptyArray:
            self.clinicInfo = nil
        }
    }
}

逻辑说明

用枚举明确定义字段的两种合法状态,解码时逐一匹配:

  • 先尝试解码成有效ClinicInfo实例
  • 失败后检查是不是空数组
  • 其他非法格式(比如非空数组)直接抛出错误,避免无效数据流入

小提示

如果后端偶尔会返回非空的数组(虽然你说明是空数组),可以在处理数组的逻辑里加个判断,确认数组为空才设为nil,否则抛出错误,避免把无效数据当成空值处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:26:28