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

Swift自定义Encoder/Decoder解码数组失败:预期数组却发现字符串

问题根源

你在自定义init(from:)时犯了两个关键错误:

  • 错误使用singleValueContainer:你尝试用它把整个JSON解码成[String: String],但你的JSON包含items数组字段,整个结构是混合了数组值的字典,根本无法被解码成纯字符串字典。
  • 解码器状态冲突:先通过keyed container解码items后,再调用singleValueContainer会干扰解码器的内部状态,导致后续解码逻辑混乱。
修复方案

核心思路是:从对应字段解码headers,而非尝试解码整个JSON结构;编码时用keyed container生成正确的JSON结构,而非单一值容器。

修改后的完整代码:

struct ServerResponse: Codable {
    var headers: [AnyHashable: Any]?
    var items: [Item]?
    
    enum CodingKeys: String, CodingKey {
        case items, headers
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        // 解码items字段
        items = try container.decodeIfPresent([Item].self, forKey: .items)
        // 从headers字段解码字符串字典,转换为[AnyHashable: Any]
        if let stringHeaders = try container.decodeIfPresent([String: String].self, forKey: .headers) {
            headers = stringHeaders.reduce(into: [AnyHashable: Any]()) { result, pair in
                result[AnyHashable(pair.key)] = pair.value
            }
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        // 编码items字段
        try container.encode(items, forKey: .items)
        // 将[AnyHashable: Any]转换为[String: String]后编码headers字段
        guard let headers = headers else { return }
        let stringHeaders = headers.reduce(into: [String: String]()) { result, pair in
            guard let key = pair.key as? String, let value = pair.value as? String else { return }
            result[key] = value
        }
        try container.encode(stringHeaders, forKey: .headers)
    }

    struct Item: Codable {
        let name: String
    }
}

// 测试代码(可添加headers字段测试完整逻辑)
let testData = """
    {
        "items": [
                    {"name": "John"},
                    {"name": "Duo"}
                ],
        "headers": {
            "Content-Type": "application/json",
            "Authorization": "Bearer token"
        }
    }
    """.data(using: .utf8)!
let decoder = JSONDecoder()
do {
    let response = try decoder.decode(ServerResponse.self, from: testData)
    print(response)
} catch {
    print(error)
}
补充说明

如果headers的值不只是字符串,你需要自定义一个可解码任意类型的容器(比如用枚举JSONValue包裹不同类型),但按照你原代码的编码逻辑,这里默认headers的键值都可转换为字符串类型。解码时用decodeIfPresent而非decode,可以兼容字段不存在的情况,更符合可选属性的定义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:03:10