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
相关产品推荐
相关产品推荐

