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

Decoder遇nil值崩溃及枚举编解码失效问题求助

嗨,我来帮你解决这两个编解码的常见问题,直接上实用方案:

一、非可选属性解码遇nil崩溃的解决方案

既然不能把属性改成可选类型,也没法用try-catch兜底,那核心思路就是在解码阶段手动处理null值,给非可选属性注入默认值,有两种常用方式:

方式1:手动实现init(from:)方法

直接在类的解码初始化方法里,对每个非可选字段用decodeIfPresent替代decode,再通过??赋予默认值。这样即使后端返回null,也不会触发崩溃,属性依然保持非可选类型:

class User: Codable {
    // 非可选属性,无需改为?
    let username: String
    let age: Int
    let score: Double

    enum CodingKeys: String, CodingKey {
        case username, age, score
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 给每个字段设置默认值,处理null场景
        username = try container.decodeIfPresent(String.self, forKey: .username) ?? "匿名用户"
        age = try container.decodeIfPresent(Int.self, forKey: .age) ?? 18
        score = try container.decodeIfPresent(Double.self, forKey: .score) ?? 0.0
    }
}

方式2:用属性包装器简化代码(适合多属性场景)

如果类的属性很多,手动写init(from:)会很繁琐,可以自定义一个@Default属性包装器,让非可选属性自动使用默认值解码:

// 定义协议,要求类型提供默认值
protocol DefaultValueProvider {
    static var defaultValue: Self { get }
}

// 给基础类型扩展默认值
extension String: DefaultValueProvider {
    static var defaultValue: String { "" }
}
extension Int: DefaultValueProvider {
    static var defaultValue: Int { 0 }
}
extension Double: DefaultValueProvider {
    static var defaultValue: Double { 0.0 }
}

// 自定义属性包装器
@propertyWrapper
struct Default<T: Codable & DefaultValueProvider> {
    var wrappedValue: T
}

// 让包装器支持Codable
extension Default: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decodeIfPresent(T.self) ?? T.defaultValue
    }
}

// 使用示例
class User: Codable {
    let username: String // 如果需要自定义默认值,还是用方式1
    @Default var age: Int // 自动用Int的默认值0
    @Default var score: Double // 自动用Double的默认值0.0
}
二、枚举编解码异常的修复方案

枚举编解码出问题,通常是原始值不匹配或关联值格式不兼容导致的,分两种情况处理:

情况1:带原始值的枚举(如String/Int)

如果后端返回的原始值不在枚举的case列表中,默认解码会崩溃,这时候需要自定义解码逻辑,给未知值设置默认case:

enum Gender: String, Codable {
    case male, female, other

    // 自定义解码,处理未知原始值
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let rawValue = try container.decode(String.self)
        // 如果原始值不存在,默认用.other
        self = Gender(rawValue: rawValue) ?? .other
    }
}

情况2:带关联值的枚举

带关联值的枚举无法自动合成Codable(或合成的格式和后端不匹配),必须手动实现init(from:)和encode(to:):

enum PaymentMethod: Codable {
    case creditCard(number: String, expiry: String)
    case paypal(email: String)

    // 定义编码需要的Key
    enum CodingKeys: String, CodingKey {
        case type, number, expiry, email
    }

    // 自定义解码逻辑
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .type)
        
        switch type {
        case "credit_card":
            let number = try container.decode(String.self, forKey: .number)
            let expiry = try container.decode(String.self, forKey: .expiry)
            self = .creditCard(number: number, expiry: expiry)
        case "paypal":
            let email = try container.decode(String.self, forKey: .email)
            self = .paypal(email: email)
        default:
            // 可以抛自定义错误,或者设置默认case(比如case unknown)
            throw DecodingError.dataCorruptedError(
                forKey: .type, 
                in: container, 
                debugDescription: "不支持的支付方式:\(type)"
            )
        }
    }

    // 自定义编码逻辑
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        
        switch self {
        case .creditCard(let number, let expiry):
            try container.encode("credit_card", forKey: .type)
            try container.encode(number, forKey: .number)
            try container.encode(expiry, forKey: .expiry)
        case .paypal(let email):
            try container.encode("paypal", forKey: .type)
            try container.encode(email, forKey: .email)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:01:26