Swift Decodable如何实现支持Stripe返回动态data数组的通用Request结构体
问题原因
Decodable要求所有属性的类型都必须遵守Decodable协议,Any没有遵守该协议,所以编译器会抛出不遵守协议的错误。
解决方案1:使用泛型结构体(推荐)
Stripe的同个列表接口返回的data数组内元素类型是固定的,调用接口时你可以明确知道当前接口对应的业务对象类型,用泛型实现通用结构最合理。
首先先定义你需要的业务模型,比如退款模型:
// 先定义退款模型,遵守Decodable struct Refund: Decodable { let id: String let object: String let amount: Int let balance_transaction: String? let charge: String let created: Int let currency: String // 其他需要的字段按需定义即可 } // 同理可定义Card等其他业务模型
然后将原来的响应结构体改成泛型(这里将结构体名改为StripeListResponse更符合语义,你也可以保留原来的Request命名):
struct StripeListResponse<T: Decodable>: Decodable { var object: String var url: String var has_more: Bool var data: [T] }
使用的时候,指定泛型类型即可:
// 解码退款列表响应时 let decoder = JSONDecoder() let refundList = try decoder.decode(StripeListResponse<Refund>.self, from: jsonData) // 解码卡片列表时,泛型替换为Card即可 let cardList = try decoder.decode(StripeListResponse<Card>.self, from: jsonData)
解决方案2:多类型枚举适配混合元素场景
如果确实存在data数组内同时包含多种不同类型对象的极端场景,可以定义一个枚举承载所有可能的业务类型,根据Stripe对象自带的object字段判断解码类型:
enum StripeObject: Decodable { case refund(Refund) case card(Card) // 其他可能的类型按需添加 // 手动实现解码逻辑 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let objectType = try container.decode(String.self, forKey: .object) switch objectType { case "refund": let refund = try Refund(from: decoder) self = .refund(refund) case "card": let card = try Card(from: decoder) self = .card(card) default: // 处理未知类型,也可以抛自定义错误 throw DecodingError.dataCorruptedError(forKey: .object, in: container, debugDescription: "未知的对象类型 \(objectType)") } } enum CodingKeys: String, CodingKey { case object } }
此时你的响应结构体可以定义为:
struct StripeListResponse: Decodable { var object: String var url: String var has_more: Bool var data: [StripeObject] }
使用时通过枚举匹配获取对应类型的对象即可:
for item in response.data { switch item { case .refund(let refund): // 处理退款对象 print(refund.amount) case .card(let card): // 处理卡片对象 print(card.last4) } }
内容的提问来源于stack exchange,提问作者user11371080
相关产品推荐
相关产品推荐

