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

如何使用Swift Codable框架拒绝JSON中的未知键数据?

在Swift中让JSONDecoder拒绝未知键的方法

你遇到的问题核心原因是:当你通过decoder.container(keyedBy: CodingKeys.self)获取容器时,该容器只会保留你定义的CodingKeys对应的键,未知键会被自动过滤,因此你无法通过这个容器捕获到unexpected_key这类非法字段。

要实现拒绝未知键的需求,有两种高效可行的方案:

方案1:自定义AnyCodingKey捕获所有键

自定义一个能匹配任意字符串键的CodingKey类型,先获取包含所有键的容器,逐一验证每个键是否属于合法的CodingKeys,发现非法键则抛出错误。

修改后的完整代码如下:

private struct DataRecord {
    var greeting: String = "Hello"
}

// 自定义可匹配任意键的CodingKey
private struct AnyCodingKey: CodingKey {
    var stringValue: String
    var intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = nil
    }
    
    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}

extension DataRecord: Codable {
    enum CodingKeys: CodingKey, CaseIterable {
        case greeting
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(greeting, forKey: .greeting)
    }
        
    init(from decoder: Decoder) throws {
        // 先获取包含所有键的容器
        let allKeysContainer = try decoder.container(keyedBy: AnyCodingKey.self)
        let validKeyStrings = Set(CodingKeys.allCases.map { $0.stringValue })
        
        // 遍历所有键,检查是否合法
        for key in allKeysContainer.allKeys {
            guard validKeyStrings.contains(key.stringValue) else {
                throw DecodingError.dataCorrupted(.init(
                    codingPath: decoder.codingPath + [key],
                    debugDescription: "发现未知键:\(key.stringValue)"
                ))
            }
        }
        
        // 用合法的CodingKeys容器解码目标字段
        let container = try decoder.container(keyedBy: CodingKeys.self)
        greeting = try container.decode(String.self, forKey: .greeting)
    }
}

func importData() {
    let stringData = """
{
    "greeting": "Hello World!",
    "unexpected_key": "This should fail!"
}
"""
    let data = stringData.data(using:.utf8)!
    let decoder = JSONDecoder()
    
    do {
        let record = try decoder.decode(DataRecord.self, from: data)
        print(record.greeting)
    } catch {
        fatalError("解码失败:\(error.localizedDescription)")
    }
}

方案2:利用keyDecodingStrategy自定义策略(Swift 5.7+)

从Swift 5.7开始,无需手动实现init(from:),通过自定义keyDecodingStrategy即可检测未知键:

private struct DataRecord {
    var greeting: String = "Hello"
}

extension DataRecord: Codable {
    enum CodingKeys: CodingKey, CaseIterable {
        case greeting
    }
}

func importData() {
    let stringData = """
{
    "greeting": "Hello World!",
    "unexpected_key": "This should fail!"
}
"""
    let data = stringData.data(using:.utf8)!
    let decoder = JSONDecoder()
    
    // 自定义键解码策略,拦截未知键
    decoder.keyDecodingStrategy = .custom { codingPath in
        let lastKey = codingPath.last!
        let validKeys = Set(DataRecord.CodingKeys.allCases.map { $0.stringValue })
        
        guard validKeys.contains(lastKey.stringValue) else {
            throw DecodingError.dataCorrupted(.init(
                codingPath: codingPath,
                debugDescription: "发现未知键:\(lastKey.stringValue)"
            ))
        }
        
        return lastKey
    }
    
    do {
        let record = try decoder.decode(DataRecord.self, from: data)
        print(record.greeting)
    } catch {
        fatalError("解码失败:\(error.localizedDescription)")
    }
}

两种方案都能在遇到未知键时触发错误,满足你期望importData方法失败的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:43:25