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

如何定义含可变字段的Swift Codable结构体?求data字段类型

处理Swift Codable中字段不固定的数组类型

根据你提供的JSON响应,data数组内的元素字段完全不固定,以下是几种适合的类型选择,可根据实际需求挑选:

方案一:固定值类型(所有value为字符串)

如果确定data元素的value都是字符串类型,直接使用[[String: String]]即可,无需额外解码逻辑:

struct ResponseData: Codable {
    let dataRes: [DataResStruct]
    struct DataResStruct: Codable {
        let data: [[String: String]]
        let key: String
    }
}

这种方式最简洁,能直接通过JSONDecoder完成解析,适合仅需读取数据、无需类型安全校验的场景。

方案二:支持任意值类型

如果data元素的value可能包含字符串、数字、布尔等多种类型,可自定义一个AnyCodable类型来兼容所有Codable类型:

struct AnyCodable: Codable {
    let value: Any

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let int = try? container.decode(Int.self) {
            value = int
        } else if let string = try? container.decode(String.self) {
            value = string
        } else if let bool = try? container.decode(Bool.self) {
            value = bool
        } else if let double = try? container.decode(Double.self) {
            value = double
        } else if let array = try? container.decode([AnyCodable].self) {
            value = array.map { $0.value }
        } else if let dict = try? container.decode([String: AnyCodable].self) {
            value = dict.mapValues { $0.value }
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "不支持的类型")
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch value {
        case let int as Int: try container.encode(int)
        case let string as String: try container.encode(string)
        case let bool as Bool: try container.encode(bool)
        case let double as Double: try container.encode(double)
        case let array as [Any]: try container.encode(array.map(AnyCodable.init))
        case let dict as [String: Any]: try container.encode(dict.mapValues(AnyCodable.init))
        default: throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: container.codingPath, debugDescription: "不支持的类型"))
        }
    }
}

使用时将data类型设为[[String: AnyCodable]]:

struct ResponseData: Codable {
    let dataRes: [DataResStruct]
    struct DataResStruct: Codable {
        let data: [[String: AnyCodable]]
        let key: String
    }
}

这种方案能兼容任意结构的data元素,适合完全不确定响应结构的场景。

方案三:类型安全的枚举关联值

如果业务上可以通过外层的key字段区分data的结构(比如key="1"对应一种结构,key="2"对应另一种),可以用枚举关联值实现类型安全解析:

首先定义对应不同结构的模型:

struct DataItemType1: Codable {
    let key1_d: String
    let key2_d: String
    let key3_d: String
}

struct DataItemType2A: Codable {
    let key1_A: String
    let key2_A: String
    let key3_A: String
    let key4_A: String
    let key5_A: String
}

struct DataItemType2B: Codable {
    let key1_C: String
}

然后定义枚举并自定义解码逻辑:

enum DataItem: Codable {
    case type1(DataItemType1)
    case type2A(DataItemType2A)
    case type2B(DataItemType2B)

    func encode(to encoder: Encoder) throws {
        switch self {
        case .type1(let item): try item.encode(to: encoder)
        case .type2A(let item): try item.encode(to: encoder)
        case .type2B(let item): try item.encode(to: encoder)
        }
    }
}

struct ResponseData: Codable {
    let dataRes: [DataResStruct]
    struct DataResStruct: Codable {
        let data: [DataItem]
        let key: String

        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            key = try container.decode(String.self, forKey: .key)
            
            let rawData = try container.decode([[String: String]].self, forKey: .data)
            var items = [DataItem]()
            
            switch key {
            case "1":
                for rawItem in rawData {
                    let itemData = try JSONSerialization.data(withJSONObject: rawItem)
                    let item = try JSONDecoder().decode(DataItemType1.self, from: itemData)
                    items.append(.type1(item))
                }
            case "2":
                for rawItem in rawData {
                    let itemData = try JSONSerialization.data(withJSONObject: rawItem)
                    if let itemA = try? JSONDecoder().decode(DataItemType2A.self, from: itemData) {
                        items.append(.type2A(itemA))
                    } else if let itemB = try? JSONDecoder().decode(DataItemType2B.self, from: itemData) {
                        items.append(.type2B(itemB))
                    } else {
                        throw DecodingError.dataCorruptedError(forKey: .data, in: container, debugDescription: "无法识别key=2对应的data元素类型")
                    }
                }
            default:
                throw DecodingError.dataCorruptedError(forKey: .key, in: container, debugDescription: "未知的key值")
            }
            
            data = items
        }
        
        enum CodingKeys: String, CodingKey {
            case data, key
        }
    }
}

这种方案能在解析时保证类型安全,后续处理data时可以通过枚举匹配做针对性逻辑,适合有明确业务区分的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:42:51