如何为Destination实现带手动解码的Enum类型?
问题描述
给定如下JSON响应:
{ "Details": { "Attachments": [], "place": { "destination": { "type": "international", "Id": "superman", "locationType": "City", "Name": "Kent" }, "package": 52.32, "description": "Dinner" } } }
该响应中,destination节点仅type为必填项,其余参数均为可选,且返回的参数会随type的不同而变化(例如type为international时返回country、id、currency等)。
目前使用如下Decodable结构体处理:
public struct Destination: Decodable, Equatable { public let Id: String? public let Name: String? public let city: String? public let locationType: String? public let pinCode: String? public let country: String? public let state: String? public let currency: String? public let language: String? public let type: Type }
希望将其改为带关联值的枚举形式,示例如下:
enum Destination { case international(country: String, id: String, currency: String) case national(state: String, language: String, pincode: String) }
请问该如何着手实现这种方式的手动解码?
实现步骤
要实现带关联值的枚举手动解码,需让枚举遵循Decodable协议,并手动实现init(from decoder: Decoder)方法,具体步骤如下:
1. 完善枚举定义
首先定义对应type字段的枚举,再完善带关联值的Destination枚举(关联值设为可选类型,适配JSON中字段可选的特性):
// 对应type字段的可选值 enum DestinationType: String, Decodable { case international case national } // 带关联值的目标枚举,遵循Decodable和Equatable enum Destination: Decodable, Equatable { case international(id: String?, name: String?, locationType: String?, country: String?, currency: String?) case national(id: String?, name: String?, locationType: String?, state: String?, language: String?, pinCode: String?) }
2. 手动实现解码逻辑
在Destination枚举内部添加CodingKeys和init(from decoder: Decoder)方法,按type分支解析对应关联值:
enum Destination: Decodable, Equatable { // ... 省略已定义的case // 定义所有可能用到的JSON字段Key enum CodingKeys: String, CodingKey { case type, Id, Name, locationType, country, currency, state, language, pinCode } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 先解析必填的type字段,确定枚举分支 let type = try container.decode(DestinationType.self, forKey: .type) switch type { case .international: // 解析国际类型对应的可选字段 let id = try container.decodeIfPresent(String.self, forKey: .Id) let name = try container.decodeIfPresent(String.self, forKey: .Name) let locationType = try container.decodeIfPresent(String.self, forKey: .locationType) let country = try container.decodeIfPresent(String.self, forKey: .country) let currency = try container.decodeIfPresent(String.self, forKey: .currency) self = .international(id: id, name: name, locationType: locationType, country: country, currency: currency) case .national: // 解析国内类型对应的可选字段 let id = try container.decodeIfPresent(String.self, forKey: .Id) let name = try container.decodeIfPresent(String.self, forKey: .Name) let locationType = try container.decodeIfPresent(String.self, forKey: .locationType) let state = try container.decodeIfPresent(String.self, forKey: .state) let language = try container.decodeIfPresent(String.self, forKey: .language) let pinCode = try container.decodeIfPresent(String.self, forKey: .pinCode) self = .national(id: id, name: name, locationType: locationType, state: state, language: language, pinCode: pinCode) } } }
3. 配合上层结构体解码
如果上层的Place、Details等结构体需要解码,直接定义并包含Destination类型属性即可:
struct Place: Decodable, Equatable { let destination: Destination let package: Double let description: String } struct Details: Decodable, Equatable { let Attachments: [String] let place: Place } struct RootResponse: Decodable, Equatable { let Details: Details }
关键注意点
- 所有可选字段必须用
decodeIfPresent解析,避免因字段缺失导致解码失败 CodingKeys需包含所有可能出现的JSON字段,确保能从容器中取出对应值- 如果需要严格校验某些必填关联值(比如
international类型必须有country),可在对应分支中添加判断,抛出自定义DecodingError
内容的提问来源于stack exchange,提问作者iosDev_1205
相关产品推荐
相关产品推荐

