Swift中如何解码不同类型的JSONRPC消息?
解决JSON-RPC消息中Result字段多类型兼容解码问题
核心思路
通过自定义枚举类型处理result的两种可能类型(字符串/字典),同时借助AnyCodable辅助类型实现任意类型的Codable兼容,既满足解码需求,又不破坏现有功能。
步骤1:定义兼容多类型的枚举与辅助结构体
// 处理Result的两种类型 enum RPCResult: Codable { case string(String) case dictionary([String: Any]) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 优先尝试解码为字符串 if let str = try? container.decode(String.self) { self = .string(str) } // 否则尝试解码为字典(借助AnyCodable处理任意值类型) else if let codableDict = try? container.decode([String: AnyCodable].self) { self = .dictionary(codableDict.mapValues { $0.value }) } // 不支持的类型抛出错误 else { throw DecodingError.typeMismatch( RPCResult.self, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "不支持的Result类型" ) ) } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let str): try container.encode(str) case .dictionary(let dict): let codableDict = dict.mapValues(AnyCodable.init) try container.encode(codableDict) } } } // 辅助结构体:实现任意Codable类型的包装 struct AnyCodable: Codable { let value: Any init<T>(_ value: T) where T: Codable { self.value = value } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 依次尝试解码常见类型 if let int = try? container.decode(Int.self) { self.value = int } else if let bool = try? container.decode(Bool.self) { self.value = bool } else if let string = try? container.decode(String.self) { self.value = string } else if let double = try? container.decode(Double.self) { self.value = double } else if let array = try? container.decode([AnyCodable].self) { self.value = array.map { $0.value } } else if let dict = try? container.decode([String: AnyCodable].self) { self.value = dict.mapValues { $0.value } } else { throw DecodingError.typeMismatch( AnyCodable.self, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "不支持的类型" ) ) } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case let int as Int: try container.encode(int) case let bool as Bool: try container.encode(bool) case let string as String: try container.encode(string) case let double as Double: try container.encode(double) case let array as [Any]: try container.encode(array.map(AnyCodable.init)) case let dict as [String: Any]: try container.encode(dict.mapValues(AnyCodable.init)) default: throw EncodingError.invalidValue( value, EncodingError.Context( codingPath: encoder.codingPath, debugDescription: "不支持的类型" ) ) } } }
步骤2:修改原JsonRPCMessage结构体
更新结构体,将result字段替换为自定义枚举类型,并补充params字段(兼容示例中的通知类消息):
struct JsonRPCMessage: Codable { let jsonrpc: String let result: RPCResult? let method: String? let id: Int? let params: [[String: Any]]? // 处理通知类消息的参数 }
步骤3:解码并处理不同类型的Result
解码后通过枚举匹配判断result类型,分别处理字符串或字典场景:
do { let decoder = JSONDecoder() let message = try decoder.decode(JsonRPCMessage.self, from: data!) // 处理Result字段 switch message.result { case .some(.string(let str)): // 字符串类型Result,如"ok" print("字符串Result: \(str)") case .some(.dictionary(let dict)): // 字典类型Result,按需访问键值对 if let klippyConnected = dict["klippy_connected"] as? Bool { print("Klippy连接状态: \(klippyConnected)") } if let apiVersion = dict["api_version"] as? [Int] { print("API版本: \(apiVersion.joined(separator: "."))") } case .none: // 无Result字段(如通知类消息) print("无Result字段") } // 处理通知类消息(如notify_proc_stat_update) if let method = message.method, method == "notify_proc_stat_update", let params = message.params?.first { if let moonrakerStats = params["moonraker_stats"] as? [String: Any] { let cpuUsage = moonrakerStats["cpu_usage"] as? Double ?? 0 print("Moonraker CPU使用率: \(cpuUsage)%") } } } catch { print("解码错误: \(error)") }
可选:解码前提前判断Result类型
如果需要在解码前先确认result的类型,可以先将Data转为JSON对象:
if let json = try? JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any] { guard let result = json["result"] else { return } switch result { case is String: print("Result是字符串类型") case is [String: Any]: print("Result是字典类型") default: print("未知的Result类型") } }
内容的提问来源于stack exchange,提问作者charlespick
相关产品推荐
相关产品推荐

