Swift中解析含不同类型对象数组的特定JSON问题咨询
混合类型JSON数组的解码方案
不建议使用合并Header和Detail所有字段、非共通字段设为可选的聚合结构。这种方案会导致后续使用时需要大量判断可选值,类型安全度低,30个字段的规模下维护成本会非常高。更合理的方案是通过自定义Decodable实现分类型解析,具体实现步骤如下:
步骤1:定义独立的Header和Detail结构体
两个结构体分别对应头结构和明细结构,各自声明自身需要的字段即可:
struct Header: Decodable { let rectype: Int let recname: String let companyname: String let companyId: String // 其他头结构专属字段 } struct Detail: Decodable { let rectype: Int let recname: String let locationId: String let shelvingUnits: Int // 其他明细结构专属字段 }
步骤2:定义枚举承载两种类型的解析结果
用带关联值的枚举作为中间解析类型,根据rectype字段自动判断解析为对应类型:
enum ResultEntity: Decodable { case header(Header) case detail(Detail) enum CodingKeys: String, CodingKey { case rectype } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let rectype = try container.decode(Int.self, forKey: .rectype) switch rectype { case 1: self = .header(try Header(from: decoder)) case 2: self = .detail(try Detail(from: decoder)) default: throw DecodingError.dataCorruptedError( forKey: .rectype, in: container, debugDescription: "未识别的记录类型:\(rectype)" ) } } }
步骤3:自定义APIResponse的解码逻辑
直接将数组拆分出头和明细两个明确类型的属性,上层使用时无需再处理数组索引:
struct APIResponse: Decodable { let resultStatus: Int let header: Header let details: [Detail] enum CodingKeys: String, CodingKey { case resultStatus, resultEntities } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解析状态码 resultStatus = try container.decode(Int.self, forKey: .resultStatus) // 解析实体数组 let entities = try container.decode([ResultEntity].self, forKey: .resultEntities) // 校验并提取头结构 guard let first = entities.first, case .header(let header) = first else { throw DecodingError.dataCorruptedError( forKey: .resultEntities, in: container, debugDescription: "实体数组首位必须为头结构" ) } self.header = header // 提取所有明细结构,若要求非头位置必须为明细可替换为遍历判断抛错 details = entities.dropFirst().compactMap { entity in guard case .detail(let detail) = entity else { return nil } return detail } } }
该方案的优势:
- 类型完全安全:Header和Detail的专属字段无需设为可选,后续使用时不需要额外判空
- 扩展性强:后续如果新增其他类型的记录,仅需要在ResultEntity枚举中新增对应case即可
- 使用成本低:对外暴露的属性清晰明确,上层调用直接取header和details即可,不需要感知底层数组的索引规则
内容的提问来源于stack exchange,提问作者chornbe
相关产品推荐
相关产品推荐

