如何让自定义Codable类型MyColor同时支持单值编码与字典键编码?
自定义Codable结构体作为字典键生成键值对JSON的解决方案
问题背景
已实现自定义结构体MyColor的Codable协议,通过单值容器将其编码/解码为类似#001122格式的字符串,这部分功能正常。但编码[MyColor: String]类型的字典时,默认实现生成无键数组:
["#121212", "Hello", "#112233", "Bye"]
期望得到键值对形式的JSON对象:
{ "#121212": "Hello", "#112233": "Bye" }
已知遵循CodingKeyRepresentable协议可实现需求,但不清楚如何添加CodingKey并完成实现,希望同时实现:将多字段编码为单个字符串,以及将该字符串作为键生成键值形式的JSON。
解决方案
只需要让MyColor同时遵循CodingKeyRepresentable协议,复用已有的单值编码逻辑实现CodingKey转换即可,具体步骤如下:
1. 抽离字符串转换逻辑
将MyColor转成#RRGGBB字符串的逻辑抽成单独的计算属性,供Codable和CodingKeyRepresentable共用,避免代码重复。
2. 实现CodingKeyRepresentable协议
自定义一个符合CodingKey协议的类型,用颜色的十六进制字符串作为键的rawValue,实现codingKey属性返回该实例。
完整代码示例
import Foundation struct MyColor: Codable, CodingKeyRepresentable { let red: UInt8 let green: UInt8 let blue: UInt8 // 统一的十六进制字符串转换逻辑 private var hexString: String { String(format: "#%02X%02X%02X", red, green, blue) } // MARK: - Codable 实现(复用hexString) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let hex = try container.decode(String.self) // 解析十六进制字符串到RGB值(按你的现有逻辑实现即可) let hexSanitized = hex.trimmingCharacters(in: CharacterSet(charactersIn: "#")) guard hexSanitized.count == 6 else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid hex color format") } red = UInt8(hexSanitized.prefix(2), radix: 16)! green = UInt8(hexSanitized.dropFirst(2).prefix(2), radix: 16)! blue = UInt8(hexSanitized.dropFirst(4), radix: 16)! } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(hexString) } // MARK: - CodingKeyRepresentable 实现 var codingKey: some CodingKey { StringCodingKey(stringValue: hexString)! } // 自定义CodingKey类型,适配字符串键 private struct StringCodingKey: CodingKey { let stringValue: String init?(stringValue: String) { self.stringValue = stringValue } let intValue: Int? = nil init?(intValue: Int) { return nil } } } // 测试代码 let colorDict: [MyColor: String] = [ MyColor(red: 0x12, green: 0x12, blue: 0x12): "Hello", MyColor(red: 0x11, green: 0x22, blue: 0x33): "Bye" ] do { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let jsonData = try encoder.encode(colorDict) if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) } } catch { print("编码错误:\(error)") }
代码说明
hexString计算属性确保颜色转字符串的逻辑唯一,避免编码值和编码键时出现不一致的情况。StringCodingKey是轻量的CodingKey实现,仅支持字符串类型的键,完美匹配颜色的十六进制格式。codingKey属性直接返回基于hexString创建的StringCodingKey实例,这样字典编码时会自动将颜色字符串作为JSON对象的键。
运行测试代码后,会输出期望的键值对格式JSON:
{ "#121212": "Hello", "#112233": "Bye" }
内容的提问来源于stack exchange,提问作者Alex Barinov
相关产品推荐
相关产品推荐

