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

Swift中如何对含未知/可变键的字典进行Codable编码?

问题翻译

我需要向服务器发送如下格式的数据:

struct Event: Codable {
    let title: String
    let params: [String:Any]? // 这里不被允许
}

比如这些事件可以是以下任意一种形式:

let event = Event(title: "cat was pet", params:["age": 7.2])
let event = Event(title: "cat purred", params:["time": 9])
let event = Event(title: "cat drank", params:["water": "salty", "amount": 3.3])

我需要给事件添加任意数量的键值对作为params,请问能不能通过Codable实现?如果不行,该如何将params编码为JSON字符串?


问题解答

直接用[String:Any]?作为Codable结构体的属性没法实现自动编码解码,因为Any类型不遵循Codable协议。不过有几种实用的解决办法:

方案一:替换成[String:Codable]类型

如果你的params里的所有值都是JSON支持的基础类型(比如字符串、数字、布尔值),或者本身就遵循Codable,直接把属性类型改成[String:Codable]?就行,结构体可以自动实现Codable:

struct Event: Codable {
    let title: String
    let params: [String:Codable]?
}

这是最简单的方案,能覆盖绝大多数场景,毕竟服务器接收的JSON数据本来就是这些类型。

方案二:手动实现Codable的编码解码逻辑

如果一定要保留[String:Any]?的类型,可以自己写encode(to:)和init(from:)方法,手动处理params的编码:

struct Event: Codable {
    let title: String
    let params: [String:Any]?
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(title, forKey: .title)
        
        guard let params = params else { return }
        // 先把params转成JSON数据,再解码成可编码的字典
        let paramsData = try JSONSerialization.data(withJSONObject: params)
        let codableParams = try JSONDecoder().decode([String:Codable].self, from: paramsData)
        try container.encode(codableParams, forKey: .params)
    }
    
    // 如果需要解码的话,还要实现init(from:)方法
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        
        if let codableParams = try container.decodeIfPresent([String:Codable].self, forKey: .params) {
            // 把Codable字典转回[String:Any]
            let data = try JSONEncoder().encode(codableParams)
            params = try JSONSerialization.jsonObject(with: data) as? [String:Any]
        } else {
            params = nil
        }
    }
    
    enum CodingKeys: String, CodingKey {
        case title, params
    }
}

注意这里params里的内容必须是JSON支持的类型,不然JSONSerialization会报错。

方案三:把params编码成JSON字符串

如果服务器要求params字段是一个JSON格式的字符串(而非JSON对象),可以先把[String:Any]转成JSON字符串,再存入结构体:

struct Event: Codable {
    let title: String
    let params: String?
    
    // 便捷初始化方法,接收[String:Any]?
    init(title: String, params: [String:Any]?) throws {
        self.title = title
        guard let params = params else {
            self.params = nil
            return
        }
        let jsonData = try JSONSerialization.data(withJSONObject: params)
        guard let jsonString = String(data: jsonData, encoding: .utf8) else {
            throw NSError(domain: "EventError", code: 0, userInfo: [NSLocalizedDescriptionKey: "无法将params转为JSON字符串"])
        }
        self.params = jsonString
    }
}

使用的时候直接调用这个初始化方法即可:

do {
    let event = try Event(title: "cat was pet", params: ["age": 7.2])
    // 后续可以正常把Event编码成JSON发送给服务器
} catch {
    print("初始化失败:\(error)")
}

内容的提问来源于stack exchange,提问作者soleil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:23:01