如何将Swift Codable数据转换为[String: CustomStringConvertible]字典
转换失败的根本原因
两类写法的底层类型完全不同:
- 直接声明的
["a": "a", "b": 1]是Swift原生字典,存储的原生String、Int类型默认遵守CustomStringConvertible协议,所以可以直接转换成功。 - 从
JSONSerialization反序列化得到的字典,所有数值类型会被桥接为OC的NSNumber、字符串桥接为NSString,这两类桥接类型没有显式遵守Swift的CustomStringConvertible协议,因此整字典直接做类型转换会返回nil。
解决方案
方案1:遍历转换JSON序列化结果
对JSONSerialization输出的字典做逐字段转换,兼容现有实现逻辑,改造成本最低:
extension Encodable { func toStringConvertibleDict() throws -> [String: CustomStringConvertible] { let jsonData = try JSONEncoder().encode(self) guard let rawDict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else { throw NSError(domain: "CodableConvert", code: -1, userInfo: [NSLocalizedDescriptionKey: "序列化结果不是字典结构"]) } var result = [String: CustomStringConvertible]() for (key, value) in rawDict { switch value { case let str as String: result[key] = str case let num as NSNumber: // 可根据需求选择保留原生数值类型或直接取字符串 // 以下为保留原类型的写法: if num.objCType.pointee == 99 { // C语言bool类型编码 result[key] = num.boolValue } else if num.doubleValue == Double(num.intValue) { result[key] = num.intValue } else { result[key] = num.doubleValue } // 若不需要保留类型直接取字符串:result[key] = num.description default: // 按需补充嵌套字典、数组等其他类型的转换逻辑 break } } return result } } // 调用示例 let model = Model(a: "a", b: 1) let parameters = try model.toStringConvertibleDict() // 转换后直接得到[String: CustomStringConvertible]类型
方案2:自定义Encoder直接转换
不需要走JSON编解码流程,性能更高,适合不需要JSON兼容的场景:
struct DescriptionDictEncoder { func encode<T: Encodable>(_ value: T) throws -> [String: CustomStringConvertible] { let mirror = Mirror(reflecting: value) var result = [String: CustomStringConvertible]() for case let (key?, value) in mirror.children { guard let convertibleValue = value as? CustomStringConvertible else { throw NSError(domain: "DescriptionDictEncoder", code: -1, userInfo: [NSLocalizedDescriptionKey: "字段\(key)不满足CustomStringConvertible要求"]) } result[key] = convertibleValue } return result } } // 调用示例 let encoder = DescriptionDictEncoder() let parameters = try encoder.encode(Model(a: "a", b: 1))
如果需要支持嵌套Codable类型,只需要递归调用encode方法处理子节点即可。
内容的提问来源于stack exchange,提问作者Kevin Renskers
相关产品推荐
相关产品推荐

