Swift中无法访问第三方枚举实现时,优雅映射JSON字符串到枚举的方法
替代冗长Switch实现第三方枚举的JSON解码
问题场景
现有JSON数据:
{ "title": "Some title", "subtitle": "Some subtitle", "button_type": "rounded" }
当前解码逻辑中,ButtonType是来自第三方库的枚举,无法修改其源码让它遵守Codable协议,只能通过switch语句做字符串到枚举的映射:
// 该枚举来自第三方库,无法访问其实现代码 enum ButtonType { case squared case simple case rounded case normal } struct ResponseModel: Decodable { var title: String var subtitle: String var type: ButtonType enum CodingKeys: String, CodingKey { case title = "title" case subtitle = "subtitle" case type = "button_type" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) subtitle = try container.decode(String.self, forKey: .subtitle) let stringType = try container.decode(String.self, forKey: .type) switch stringType { case "squared": type = .squared case "simple": type = .simple case "rounded": type = .rounded default: type = .normal } } }
想要替代这段冗长的switch,用更优雅的方式实现映射。
可行的优雅方案
方案一:用映射字典集中管理
把字符串和枚举的对应关系放到一个静态字典里,直接通过字典查找获取枚举值,代码更简洁,后续修改映射也更方便:
struct ResponseModel: Decodable { var title: String var subtitle: String var type: ButtonType enum CodingKeys: String, CodingKey { case title, subtitle, type = "button_type" } // 集中管理字符串到枚举的映射 private static let buttonTypeMap: [String: ButtonType] = [ "squared": .squared, "simple": .simple, "rounded": .rounded ] init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) subtitle = try container.decode(String.self, forKey: .subtitle) let stringType = try container.decode(String.self, forKey: .type) // 字典查找,找不到就用默认的.normal type = Self.buttonTypeMap[stringType] ?? .normal } }
方案二:给枚举加扩展封装解析逻辑
把解析逻辑封装到ButtonType的扩展里,让ResponseModel的解码代码更清爽,同时解析逻辑可以复用:
// 给第三方枚举加扩展,封装解析方法 extension ButtonType { static func parse(from rawValue: String) -> ButtonType { switch rawValue { case "squared": return .squared case "simple": return .simple case "rounded": return .rounded default: return .normal } } } struct ResponseModel: Decodable { var title: String var subtitle: String var type: ButtonType enum CodingKeys: String, CodingKey { case title, subtitle, type = "button_type" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) subtitle = try container.decode(String.self, forKey: .subtitle) let stringType = try container.decode(String.self, forKey: .type) // 直接调用扩展方法解析 type = ButtonType.parse(from: stringType) } }
方案三:用辅助解码类型封装逻辑
创建一个遵守Decodable的辅助类型,专门负责ButtonType的解码,让ResponseModel的职责更单一:
// 辅助解码类型,专门处理ButtonType的解码 private struct ButtonTypeDecoder: Decodable { let type: ButtonType init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let rawValue = try container.decode(String.self) type = [ "squared": ButtonType.squared, "simple": .simple, "rounded": .rounded ][rawValue] ?? .normal } } struct ResponseModel: Decodable { var title: String var subtitle: String var type: ButtonType enum CodingKeys: String, CodingKey { case title, subtitle, type = "button_type" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) subtitle = try container.decode(String.self, forKey: .subtitle) // 解码辅助类型,再取出枚举值 let decoder = try container.decode(ButtonTypeDecoder.self, forKey: .type) type = decoder.type } }
内容的提问来源于stack exchange,提问作者stompy
相关产品推荐
相关产品推荐

