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

Swift Codable解析含可选互斥字段JSON返回nil问题求解

问题原因
  • 核心错误:ShortLinkData自定义解码方法中,result字段使用decode(_:forKey:)方法解析,该方法要求目标字段必须存在于JSON中,否则直接抛出解码错误。当接口返回失败响应时JSON无result字段,解码直接失败,被外层try?转换为nil,因此始终拿不到解析结果。
  • 潜在冲突:自定义的结果结构体命名为Result,和Swift标准库中的Result枚举重名,容易引发类型歧义错误。
  • 冗余代码:两个模型都手写了全量init(from decoder:)实现,只要CodingKeys配置正确,Codable会自动生成正确的解码实现,多余手写代码反而增加出错概率。
修正方案

方案1:最小改动适配

只需要调整result字段的解码方式,修正类型命名即可,不需要改动整体结构:

struct ShortLinkData: Codable {
    let ok: Bool
    let result: ShortLinkResult?
    let errorCode: Int?
    let error: String?
    
    private enum CodingKeys : String, CodingKey {
        case ok, result
        case errorCode = "error_code"
        case error
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        ok = try container.decode(Bool.self, forKey: .ok)
        // 改为decodeIfPresent,字段不存在时返回nil而非抛错
        result = try container.decodeIfPresent(ShortLinkResult.self, forKey: .result)
        errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
        error = try container.decodeIfPresent(String.self, forKey: .error)
    }
}

// 重命名避免和系统Result类型冲突
struct ShortLinkResult: Codable {
    let code, shortLink: String
    let fullShortLink: String
    let shortLink2: String
    let fullShortLink2: String
    let shortLink3: String
    let fullShortLink3: String
    let shareLink: String
    let fullShareLink: String
    let originalLink: String

    enum CodingKeys: String, CodingKey {
        case code
        case shortLink = "short_link"
        case fullShortLink = "full_short_link"
        case shortLink2
        case fullShortLink2 = "full_short_link2"
        case shortLink3
        case fullShortLink3 = "full_short_link3"
        case shareLink = "share_link"
        case fullShareLink = "full_share_link"
        case originalLink = "original_link"
    }
    // 无需手写init,Codable自动生成的实现可正常工作
}

解析时不要用强制解包,避免异常崩溃:

let str = String(decoding: data, as: UTF8.self)
print(str)
do {
    let shortURL = try JSONDecoder().decode(ShortLinkData.self, from: data)
    if shortURL.ok {
        // 处理成功逻辑,直接取result字段即可
        print("短链接地址:\(shortURL.result?.fullShortLink ?? "")")
    } else {
        // 处理失败逻辑,取错误码和错误信息
        print("请求失败:\(shortURL.error ?? ""),错误码:\(shortURL.errorCode ?? -1)")
    }
} catch {
    print("解析失败:\(error.localizedDescription)")
}

方案2:枚举类型严谨实现(推荐)

由于成功、失败返回的字段完全互斥,可以使用带关联值的枚举实现模型,从类型系统层面避免同时拿到结果和错误的异常状态:

enum ShortLinkResponse: Codable {
    case success(result: ShortLinkResult)
    case failure(errorCode: Int, errorMsg: String)
    
    private enum CodingKeys: String, CodingKey {
        case ok, result, error_code, error
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let isSuccess = try container.decode(Bool.self, forKey: .ok)
        if isSuccess {
            let result = try container.decode(ShortLinkResult.self, forKey: .result)
            self = .success(result: result)
        } else {
            let code = try container.decode(Int.self, forKey: .error_code)
            let msg = try container.decode(String.self, forKey: .error)
            self = .failure(errorCode: code, errorMsg: msg)
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .success(let result):
            try container.encode(true, forKey: .ok)
            try container.encode(result, forKey: .result)
        case .failure(let code, let msg):
            try container.encode(false, forKey: .ok)
            try container.encode(code, forKey: .error_code)
            try container.encode(msg, forKey: .error)
        }
    }
}

// ShortLinkResult 结构体同方案1
struct ShortLinkResult: Codable {
    let code, shortLink: String
    let fullShortLink: String
    let shortLink2: String
    let fullShortLink2: String
    let shortLink3: String
    let fullShortLink3: String
    let shareLink: String
    let fullShareLink: String
    let originalLink: String

    enum CodingKeys: String, CodingKey {
        case code
        case shortLink = "short_link"
        case fullShortLink = "full_short_link"
        case shortLink2
        case fullShortLink2 = "full_short_link2"
        case shortLink3
        case fullShortLink3 = "full_short_link3"
        case shareLink = "share_link"
        case fullShareLink = "full_share_link"
        case originalLink = "original_link"
    }
}

使用时直接通过switch分支处理,不需要解包可选值:

do {
    let response = try JSONDecoder().decode(ShortLinkResponse.self, from: data)
    switch response {
    case .success(let result):
        print("解析成功,短链接:\(result.fullShortLink)")
    case .failure(let code, let msg):
        print("请求失败,错误码\(code):\(msg)")
    }
} catch {
    print("解析失败:\(error.localizedDescription)")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:27:17