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

Swift JSON编解码:UUID为键的空字典被编码为数组致解码失败如何解决?

Swift JSONEncoder/Decoder 空字典编码异常问题

我在使用Swift的JSONEncoder/Decoder做JSON编解码时遇到问题:我的JSON里有一个以UUID为键的字典,当这个字典为空时,解码会报错:

Expected to decode Array but found a dictionary instead.

分析后发现,Swift会根据字典键的类型对空字典生成不同的编码结果,下面的最小示例可以复现这个问题:

import Foundation

typealias Key = UUID // or String

struct Foo: Codable {
    let data: [Key: String]
}

let foo = Foo(data: [:])
let encodedData = try JSONEncoder().encode(foo)
let foo2 = try JSONDecoder().decode(Foo.self, from: encodedData)

print(String(decoding: encodedData, as: UTF8.self))

现象对比:

  • 当Key为UUID时,编码结果是:
    {"data":[]}
    
  • 当Key为String时,编码结果是:
    {"data":{} }
    

我希望所有情况下编码结果都是{"data":{}},请问问题出在哪?


问题原因与解决方案

问题原因

JSON规范要求对象的键必须是字符串,Swift的默认编码逻辑中:

  • 对于String类型的键,完全符合JSON要求,空字典会被编码为空JSON对象{};
  • 对于UUID这类非字符串类型的键,默认会把字典转成[Key, Value]格式的数组来编码,空字典自然就变成了空数组[],这就导致了解码时的类型不匹配错误。

解决方案

要让UUID作为键的空字典也编码为空对象,有两种可行方案:

方案一:全局处理UUID键的字典编码

扩展KeyedEncodingContainer,强制把UUID键转成字符串后再编码为JSON对象:

extension KeyedEncodingContainer {
    mutating func encode<T>(_ value: [UUID: T], forKey key: Key) throws where T: Encodable {
        let stringKeyedDict = Dictionary(uniqueKeysWithValues: value.map { ($0.uuidString, $1) })
        try encode(stringKeyedDict, forKey: key)
    }
}

这样所有以UUID为键的字典,无论是否为空,都会被编码为JSON对象,空字典的结果就是{}。

方案二:针对特定结构体自定义编解码逻辑

如果不想全局修改UUID的编码行为,可以在目标结构体中自定义编解码方法,仅处理指定属性:

struct Foo: Codable {
    let data: [UUID: String]
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        // 将UUID键转换为字符串键的字典
        let stringDict = data.mapKeys { $0.uuidString }
        try container.encode(stringDict, forKey: .data)
    }
    
    // 解码时对应将字符串键转回UUID
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let stringDict = try container.decode([String: String].self, forKey: .data)
        data = stringDict.compactMapKeys { UUID(uuidString: $0) }
    }
    
    enum CodingKeys: String, CodingKey {
        case data
    }
}

这种方式仅对Foo结构体的data属性生效,同时保证了解码时的类型一致性。


内容的提问来源于stack exchange,提问作者Tobias Müller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 10:05:21