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

Swift中如何用全局CodingKeys简化结构体JSON解码

问题描述

我有如下JSON字符串:

let jsonStringBird = """
{
    "identifier": "0001ABX",
    "name": "Doroty",
    "species_name": "Gorrion",
}
"""


let jsonStringPlane = """
{
    "identifier": "PL1803",
    "name": "Boing 777",
    "number_sits_in_plane": 8,
}
"""

同时有大量如下示例结构体:

struct Bird: Decodable {
    var identifier: String
    var name: String
    var species: String
}

struct Plane: Decodable {
    var identifier: String
    var name: String
    var sits: String
}

我希望在不修改这些结构体的前提下完成解码(避免重写init(from:)方法,也避免添加私有CodingKeys枚举)。我设想使用如下全局CodingKey枚举,但不知道如何让JSONDecoder为所有结构体使用该CodingKey(仅知晓在init(from:)中使用的方法):

enum MyCodingKeys: String, CodingKey {
    case identifier
    case name
    case species = "species_name"
    case sits = "number_sits_in_plane"
}

核心需求是尽量简化大量结构体的实现。

说明
实际项目中我使用的是CBORCoding,映射的是[Int, AnyCodable]而非[String, AnyCodable]。因此需基于JSONDecoder和CodingKeys实现,以便后续迁移至CBORCoding。


解决方案

要在不修改原有结构体的前提下实现全局键映射,你可以通过自定义JSONDecoder的userInfo,结合KeyedDecodingContainer扩展来复用全局CodingKey,具体步骤如下:

1. 定义全局键映射的标识

先定义一个userInfo的专属key,用来传递全局CodingKey的类型:

extension CodingUserInfoKey {
    static let globalCodingKeysType = CodingUserInfoKey(rawValue: "com.yourdomain.globalCodingKeysType")!
}

2. 扩展KeyedDecodingContainer,实现全局键优先解码

给KeyedDecodingContainer添加通用扩展,优先尝试使用全局CodingKey进行解码,失败则回退到默认逻辑:

extension KeyedDecodingContainer where Key == CodingKey {
    func decode<T: Decodable>(_ type: T.Type, forKey key: CodingKey) throws -> T {
        // 从userInfo中获取全局CodingKey类型
        guard let globalKeysType = userInfo[.globalCodingKeysType] as? Any.Type,
              let globalKey = (globalKeysType as? CodingKey.Type)?.init(stringValue: key.stringValue) else {
            return try decode(type, forKey: key)
        }
        
        // 优先用全局键解码,找不到则回退到原键
        do {
            return try decode(type, forKey: globalKey)
        } catch DecodingError.keyNotFound {
            return try decode(type, forKey: key)
        }
    }
}

3. 配置解码器并完成解码

使用时,给JSONDecoder的userInfo设置全局CodingKey类型,直接解码即可:

let decoder = JSONDecoder()
// 绑定全局CodingKey类型
decoder.userInfo[.globalCodingKeysType] = MyCodingKeys.self

// 解码Bird
let birdData = jsonStringBird.data(using: .utf8)!
let bird = try decoder.decode(Bird.self, from: birdData)

// 解码Plane
let planeData = jsonStringPlane.data(using: .utf8)!
let plane = try decoder.decode(Plane.self, from: planeData)

适配CBORCoding的注意事项

由于你实际使用的CBOR以Int为键,只需将全局CodingKey改为Int类型枚举,上述逻辑完全通用:

enum MyCBORCodingKeys: Int, CodingKey {
    case identifier = 1
    case name = 2
    case species = 3
    case sits = 4
}

后续迁移时,只需替换JSONDecoder为CBOR解码器,设置对应的全局CodingKey类型即可,无需修改原有结构体。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:14:50