Swift对象转Dictionary异常:数组未转为期望的字典结构
解决Codable转[String: Any]时数组变为NSArray的问题
我完全懂你的困扰——你想把Codable对象转成纯Swift原生的[String: Any]字典用于POST请求体,但现在数组部分变成了NSArray类型,这确实不符合接口要求。
问题根源
你当前的实现依赖JSONSerialization.jsonObject(with:options:)来解析JSON Data,这个方法返回的是Foundation框架的集合类型(NSDictionary、NSArray),而不是Swift原生的[String: Any]和[Any]。所以当你的模型里包含数组属性时,解析结果自然是NSArray,而不是装着Swift字典的数组。
解决方案:递归转换为Swift原生类型
我们需要写一个递归转换函数,把所有Foundation集合类型转换成对应的Swift原生类型,这样就能得到纯Swift的[String: Any]结构了。
首先添加这个转换函数:
private func convertToSwiftTypes(_ value: Any) -> Any { switch value { case let dict as NSDictionary: // 将NSDictionary转换为[String: Any],并递归转换内部元素 return dict.allKeys.reduce(into: [String: Any]()) { result, key in guard let stringKey = key as? String else { return } result[stringKey] = convertToSwiftTypes(dict[stringKey]!) } case let array as NSArray: // 将NSArray转换为Swift Array,并递归转换每个元素 return array.map { convertToSwiftTypes($0) } default: // 其他类型(字符串、数字等)直接返回 return value } }
然后修改你的NSDictionary扩展方法,调用这个转换函数:
extension NSDictionary { func encode<T>(_ value: T) throws -> [String: Any]? where T: Encodable { // 这里改用try而不是try?,避免吞掉编码错误,方便调试 let jsonData = try JSONEncoder().encode(value) guard let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? NSDictionary else { return nil } // 转换为Swift原生类型 return convertToSwiftTypes(jsonObject) as? [String: Any] } }
测试效果
现在再运行你的使用示例:
let country = Country(name: "France", cities: [Country.City(name: "Paris"), Country.City(name: "Marseille")]) if let result = try? NSDictionary().encode(country) { print(result) }
输出就会变成你期望的纯Swift结构:
["name": "France", "cities": [["name": "Paris"], ["name": "Marseille"]]]
额外建议
其实这个扩展方法和NSDictionary的实例没有任何关系,把它放在NSDictionary里有点奇怪,你可以把它改成全局函数或者一个工具类的静态方法,比如:
struct CodableConverter { static func convertToDictionary<T: Encodable>(_ value: T) throws -> [String: Any]? { let jsonData = try JSONEncoder().encode(value) guard let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? NSDictionary else { return nil } return convertToSwiftTypes(jsonObject) as? [String: Any] } private static func convertToSwiftTypes(_ value: Any) -> Any { // 同样的转换逻辑 switch value { case let dict as NSDictionary: return dict.allKeys.reduce(into: [String: Any]()) { result, key in guard let stringKey = key as? String else { return } result[stringKey] = convertToSwiftTypes(dict[stringKey]!) } case let array as NSArray: return array.map { convertToSwiftTypes($0) } default: return value } } }
这样调用起来更合理:try? CodableConverter.convertToDictionary(country)
内容的提问来源于stack exchange,提问作者Mickael Belhassen
相关产品推荐
相关产品推荐

