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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:10:45