如何根据WebSocket消息的JSON字段值解码为对应Swift对象?
根据JSON动态字段选择解码类型的解决方案
不需要强制解码两次,这里有两种实用的处理方式:
方法1:临时结构体先取类型(直观易实现)
先定义一个仅包含type字段的临时结构体,解码它拿到类型标识后,再用对应的目标结构体解码完整数据。这种方式的性能开销极低,因为第一次解码只处理单个字段:
// 临时结构体,仅用于获取type字段 struct TypeIndicator: Decodable { let type: String } // 假设你已有User、Post这类目标结构体 struct User: Decodable { let id: Int let name: String } struct Post: Decodable { let id: Int let title: String } // 修改后的动态解码函数 func decodeDynamic(_ result: Result<Response, MoyaError>) -> Result<Decodable, Error> { switch result { case .success(let response): let data = response.data do { // 第一步:获取type值 let typeInfo = try JSONDecoder().decode(TypeIndicator.self, from: data) // 第二步:根据type选择对应类型解码 switch typeInfo.type { case "user": let user = try JSONDecoder().decode(User.self, from: data) return .success(user) case "post": let post = try JSONDecoder().decode(Post.self, from: data) return .success(post) default: throw NSError(domain: "DecodeError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未知类型: \(typeInfo.type)"]) } } catch { return .failure(error) } case .failure(let error): return .failure(error) } }
方法2:自定义解码逻辑(一次解码更高效)
通过自定义解码容器,在单次解码流程中先读取type字段,再动态选择目标类型完成解码,避免重复解析整个JSON:
// 定义协议统一类型标识 protocol DynamicDecodable: Decodable { static var typeIdentifier: String { get } } // 目标结构体实现协议 struct User: DynamicDecodable { let id: Int let name: String static var typeIdentifier: String { "user" } } struct Post: DynamicDecodable { let id: Int let title: String static var typeIdentifier: String { "post" } } // 自定义错误类型 enum DynamicDecodeError: Error { case unknownType(String) } // 动态解码工具类 class DynamicDecoder { static func decode(from data: Data) throws -> DynamicDecodable { let decoder = JSONDecoder() // 先获取容器读取type字段 let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) // 根据type选择对应类型解码 switch type { case User.typeIdentifier: return try decoder.decode(User.self, from: data) case Post.typeIdentifier: return try decoder.decode(Post.self, from: data) default: throw DynamicDecodeError.unknownType(type) } } // 定义CodingKey仅用于读取type private enum CodingKeys: String, CodingKey { case type } } // 业务中调用的解码函数 func decodeDynamic(_ result: Result<Response, MoyaError>) -> Result<DynamicDecodable, Error> { switch result { case .success(let response): do { let decodedObject = try DynamicDecoder.decode(from: response.data) return .success(decodedObject) } catch { return .failure(error) } case .failure(let error): return .failure(error) } }
两种方式各有优劣:
- 方法1实现简单,适合场景单一的情况,不需要额外定义协议或工具类;
- 方法2只做一次完整解码,性能更优,适合频繁处理动态类型解码的场景,代码扩展性也更好。
内容的提问来源于stack exchange,提问作者Gargo
相关产品推荐
相关产品推荐

