如何使用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
相关产品推荐
相关产品推荐

