如何使用Swift 4 Codable解析键为Int数组式的JSON数据?
解决方案:Swift 4 Codable解析键为字符串数字的JSON
这个问题很常见——当JSON的键是字符串形式的数字时,直接用[Int: YourModel]解码会失败,因为JSON的键本质上都是字符串类型。下面给你几种可行的方案,从快速实现到优雅复用:
方案1:直接解码为[String: Document](最简单)
先定义基础的Document模型,它能直接映射JSON里的单个文档结构:
struct Document: Codable { let name: String let cmisid: String }
然后直接用字典类型解码,之后按需转换键的类型:
// 假设jsonData是你的JSON数据(从文件/网络获取的Data实例) do { let stringKeyedDict = try JSONDecoder().decode([String: Document].self, from: jsonData) // 转成Int键的字典 let intKeyedDict = stringKeyedDict.reduce(into: [Int: Document]()) { result, entry in if let intKey = Int(entry.key) { result[intKey] = entry.value } } // 或者直接转成按数字顺序排列的文档数组 let sortedDocuments = intKeyedDict.sorted { $0.key < $1.key }.map { $0.value } } catch { print("解码出错:\(error.localizedDescription)") }
这种方式不需要自定义CodingKey,适合快速实现,但如果需要多次处理这类JSON,自定义模型会更整洁。
方案2:自定义模型+动态CodingKey(优雅复用)
如果希望直接解码成[Int: Document]或整理好的数组,可以自定义一个容器模型,配合动态CodingKey来处理字符串形式的数字键:
步骤1:定义动态CodingKey
这个CodingKey可以适配任意字符串/数字键,是处理动态键的核心:
struct DynamicCodingKey: CodingKey { let stringValue: String let intValue: Int? init?(stringValue: String) { self.stringValue = stringValue self.intValue = Int(stringValue) } init?(intValue: Int) { self.stringValue = "\(intValue)" self.intValue = intValue } }
步骤2:定义容器模型
struct DocumentCollection: Codable { let documents: [Int: Document] // 可选:添加计算属性,直接得到按数字顺序排列的文档数组 var sortedDocuments: [Document] { documents.sorted { $0.key < $1.key }.map { $0.value } } // 自定义解码逻辑 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: DynamicCodingKey.self) var tempDict = [Int: Document]() // 遍历所有键,转成Int后解码对应的值 for key in container.allKeys { guard let intKey = Int(key.stringValue) else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: [key], debugDescription: "键\(key.stringValue)不是有效的整数" ) ) } let document = try container.decode(Document.self, forKey: key) tempDict[intKey] = document } documents = tempDict } // 如果需要把模型转回JSON,实现编码逻辑(可选) func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: DynamicCodingKey.self) for (intKey, document) in documents { guard let codingKey = DynamicCodingKey(stringValue: "\(intKey)") else { throw EncodingError.invalidValue( intKey, EncodingError.Context( codingPath: [], debugDescription: "无法将整数\(intKey)转换为编码键" ) ) } try container.encode(document, forKey: codingKey) } } }
步骤3:使用模型解码
do { let collection = try JSONDecoder().decode(DocumentCollection.self, from: jsonData) // 直接获取Int键的字典 print(collection.documents[0]?.name) // 输出"legaldoc.pdf" // 或者直接获取排序后的数组 let sortedDocs = collection.sortedDocuments sortedDocs.forEach { print($0.name) } } catch { print("解码失败:\(error)") }
关键说明
- JSON的键本质上都是字符串,所以Swift默认的
JSONDecoder无法直接把字符串键映射到Int类型的字典键,必须手动转换 - 动态
DynamicCodingKey是核心,它允许我们处理任意字符串形式的键,再在解码时转成需要的Int类型 - 如果不需要编码功能,可以省略
encode(to:)方法
内容的提问来源于stack exchange,提问作者Shaobai Li
相关产品推荐
相关产品推荐

