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

如何根据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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:43:31