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

Swift中如何用JSONDecoder解码可变数组里的可变对象

Swift 可变JSON数组解码解决方案

你的核心问题在于:tablesArray中的每个元素是仅包含单个表键值对的对象,但你定义的Tables结构体要求所有表属性都必须存在,这和JSON结构不匹配,导致解码失败。下面提供两种可行的解决方案:

方案一:将Tables属性改为可选类型(简单易上手)

把Tables结构体的所有表属性都设为可选类型(?),这样JSONDecoder会自动忽略不存在的键,每个Tables实例只会有一个非nil的属性:

struct Tables: Decodable {
    let settingsTable: [SettingsTable]?
    let userTable: [UserTable]?
    let memberTable: [MemberTable]?
    let personnelTable: [PersonnelTable]?
    let notificationsTable: [NotificationsTable]?
    let paymentsTable: [PaymentsTable]?
    let debitsTable: [DebitsTable]?
    let checksTable: [ChecksTable]?
    let logsTable: [LogsTable]?
    let qrsTable: [QrsTable]?
}

// 其余子表结构体(如SettingsTable)保持不变

解码后,你可以通过判断属性是否为nil来处理对应数据:

do {
    let mainData = try JSONDecoder().decode(StructMain.self, from: data!)
    for table in mainData.tablesArray {
        if let settings = table.settingsTable {
            // 处理settingsTable数据
            print("配置表数据: \(settings)")
        } else if let users = table.userTable {
            // 处理userTable数据
            print("用户表数据: \(users)")
        }
        // 其他表的判断逻辑依次添加
    }
} catch {
    print("解码失败: \(error.localizedDescription)")
}

方案二:使用枚举统一处理表类型(更优雅)

定义一个枚举来表示所有可能的表类型,通过自定义解码逻辑匹配对应的键,这样处理数据时更清晰:

1. 定义表类型枚举

enum TableType: Decodable {
    case settings([SettingsTable])
    case user([UserTable])
    case member([MemberTable])
    case personnel([PersonnelTable])
    case notifications([NotificationsTable])
    case payments([PaymentsTable])
    case debits([DebitsTable])
    case checks([ChecksTable])
    case logs([LogsTable])
    case qrs([QrsTable])
    
    // 枚举所有可能的表键
    enum CodingKeys: String, CodingKey {
        case settingsTable, userTable, memberTable, personnelTable, notificationsTable
        case paymentsTable, debitsTable, checksTable, logsTable, qrsTable
    }
    
    // 自定义解码逻辑
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 依次检查每个键是否存在,匹配对应表类型
        if let settings = try container.decodeIfPresent([SettingsTable].self, forKey: .settingsTable) {
            self = .settings(settings)
        } else if let users = try container.decodeIfPresent([UserTable].self, forKey: .userTable) {
            self = .user(users)
        } else if let members = try container.decodeIfPresent([MemberTable].self, forKey: .memberTable) {
            self = .member(members)
        } else if let personnel = try container.decodeIfPresent([PersonnelTable].self, forKey: .personnelTable) {
            self = .personnel(personnel)
        } else if let notifications = try container.decodeIfPresent([NotificationsTable].self, forKey: .notificationsTable) {
            self = .notifications(notifications)
        } else if let payments = try container.decodeIfPresent([PaymentsTable].self, forKey: .paymentsTable) {
            self = .payments(payments)
        } else if let debits = try container.decodeIfPresent([DebitsTable].self, forKey: .debitsTable) {
            self = .debits(debits)
        } else if let checks = try container.decodeIfPresent([ChecksTable].self, forKey: .checksTable) {
            self = .checks(checks)
        } else if let logs = try container.decodeIfPresent([LogsTable].self, forKey: .logsTable) {
            self = .logs(logs)
        } else if let qrs = try container.decodeIfPresent([QrsTable].self, forKey: .qrsTable) {
            self = .qrs(qrs)
        } else {
            throw DecodingError.dataCorruptedError(
                forKey: container.allKeys.first!,
                in: container,
                debugDescription: "无法识别的表类型"
            )
        }
    }
}

2. 修改StructMain的tablesArray类型

struct StructMain: Decodable {
    let result: Bool
    let message: String
    let messageType: String
    let action: String
    let tablesArray: [TableType] // 改为TableType数组
}

3. 解码并处理数据

do {
    let decoder = JSONDecoder()
    // 允许JSON中的尾随逗号(如果后端返回的JSON有多余逗号)
    decoder.allowsJSON5 = true
    let mainData = try decoder.decode(StructMain.self, from: data!)
    
    for table in mainData.tablesArray {
        switch table {
        case .settings(let settings):
            print("配置表: \(settings)")
        case .user(let users):
            print("用户表: \(users)")
        case .member(let members):
            print("成员表: \(members)")
        // 其他表类型的case依次添加
        default:
            break
        }
    }
} catch {
    print("解码错误: \(error)")
}

注意事项

  • 你的示例JSON中memberTable的最后一个键值对有多余的尾随逗号("memberStatusDescription": "Created by system",),这不符合严格JSON规范。如果无法要求后端修正,可以设置JSONDecoder().allowsJSON5 = true来允许这种格式。
  • 如果后续新增表类型,只需在枚举中添加对应的case和CodingKeys即可,扩展性强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 05:46:01