You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 07:10:15