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

Swift中自定义Decoder初始化:从JSON动态键获取Option的id值

自定义Decodable初始化实现动态id字段解析

需求说明

需要为指定结构体实现自定义Decodable初始化逻辑,适配JSON中Option的id字段动态变化的场景:

  • 若OptionContainer包含randomKey字段,则用该字段的值作为键,提取对应值作为Option.id
  • 若randomKey不存在,则直接使用Option的id字段

原结构体代码

struct OptionContainer: Decodable {
    let randomKey: String?
    let id: String
    let text: String

    struct Option: Decodable {
        let id: String
        let text: String
    }
}

目标JSON数据

[
    {
        "id": "123",
        "text": "text",
        "randomKey": "level",
        "options": [
            {
                "text": "Hello",
                "level": "1"
            },
            {
                "text": "Hello2",
                "level": "2"
            },
            {
                "text": "Hello3",
                "level": "3"
            }
        ]
    },
    {
        "id": "222",
        "text": "text2",
        "options": [
            {
                "text": "Hello",
                "id": "1"
            },
            {
                "text": "Hello2",
                "id": "2"
            },
            {
                "text": "Hello3",
                "id": "3"
            }
        ]
    }
]

完整实现代码

首先为OptionContainer补充options属性,同时为Option实现带动态id字段名参数的自定义初始化:

struct OptionContainer: Decodable {
    let randomKey: String?
    let id: String
    let text: String
    let options: [Option]

    struct Option: Decodable {
        let id: String
        let text: String
        
        // 接收动态id字段名的自定义初始化方法
        init(from decoder: Decoder, idKey: String) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.text = try container.decode(String.self, forKey: .text)
            
            // 尝试读取动态指定的id字段,失败则fallback到默认id字段
            if let dynamicId = try? container.decode(String.self, forKey: CodingKeys(stringValue: idKey)!) {
                self.id = dynamicId
            } else {
                self.id = try container.decode(String.self, forKey: .id)
            }
        }
        
        enum CodingKeys: String, CodingKey {
            case id, text
        }
    }
    
    // OptionContainer的自定义初始化
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.text = try container.decode(String.self, forKey: .text)
        self.randomKey = try container.decodeIfPresent(String.self, forKey: .randomKey)
        
        // 确定Option要使用的id字段名
        let targetIdKey = self.randomKey ?? "id"
        
        // 解析options数组,为每个Option传入目标id字段名
        var optionsContainer = try container.nestedUnkeyedContainer(forKey: .options)
        var parsedOptions = [Option]()
        
        while !optionsContainer.isAtEnd {
            let optionDecoder = try optionsContainer.superDecoder()
            let option = try Option(from: optionDecoder, idKey: targetIdKey)
            parsedOptions.append(option)
        }
        
        self.options = parsedOptions
    }
    
    enum CodingKeys: String, CodingKey {
        case id, text, randomKey, options
    }
}

关键逻辑说明

  1. Option的动态解析:新增带idKey参数的初始化方法,优先按传入的字段名读取id值;若动态字段不存在,则使用默认的id字段(可根据需求调整错误处理逻辑,比如直接抛出解码错误)。
  2. OptionContainer的统筹解析:先解析自身基础字段确定目标id键名,再遍历options数组的解码器,为每个Option传入对应键名完成动态解析。

验证测试

使用JSONDecoder解析目标JSON,验证结果:

let jsonData = """
[
    {
        "id": "123",
        "text": "text",
        "randomKey": "level",
        "options": [
            {
                "text": "Hello",
                "level": "1"
            },
            {
                "text": "Hello2",
                "level": "2"
            },
            {
                "text": "Hello3",
                "level": "3"
            }
        ]
    },
    {
        "id": "222",
        "text": "text2",
        "options": [
            {
                "text": "Hello",
                "id": "1"
            },
            {
                "text": "Hello2",
                "id": "2"
            },
            {
                "text": "Hello3",
                "id": "3"
            }
        ]
    }
]
""".data(using: .utf8)!

do {
    let containers = try JSONDecoder().decode([OptionContainer].self, from: jsonData)
    containers.forEach { container in
        print("容器ID: \(container.id)")
        print("选项ID列表: \(container.options.map { $0.id })")
    }
} catch {
    print("解码错误: \(error)")
}

输出结果:

容器ID: 123
选项ID列表: ["1", "2", "3"]
容器ID: 222
选项ID列表: ["1", "2", "3"]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:25:38