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

Swift 4中使用Codable处理含AnyObject的基实体类问题

处理Swift 4中Codable解析动态类型的通用响应

这种通用API响应的场景太常见了——先返回基础状态信息,再根据状态返回不同类型的数据。我给你提供两种实用的方案,你可以根据自己的需求选择:

方案一:分两步解析(推荐,简单直观)

核心思路是先解析出status和statusDescription,再根据状态值动态解析records对应的具体类型:

  1. 定义基础状态结构体:先只解析状态相关字段
struct BaseStatusResponse: Codable {
    let status: Int?
    let statusDescription: String?
}
  1. 分阶段解析JSON数据
// 假设你拿到的API返回数据是data: Data
do {
    // 第一步:解析基础状态信息
    let baseResponse = try JSONDecoder().decode(BaseStatusResponse.self, from: data)
    
    // 初始化你的基类实体
    let finalResponse = entStatusAndDescription()
    finalResponse.status = baseResponse.status
    finalResponse.statusDescription = baseResponse.statusDescription
    
    // 第二步:根据status动态解析records
    guard let status = baseResponse.status else {
        // 处理状态为空的情况
        return
    }
    
    switch status {
    case 200:
        // 解析成客户实体(假设你的客户实体是Customer,已经实现Codable)
        let fullResponse = try JSONDecoder().decode(Response<Customer>.self, from: data)
        finalResponse.records = fullResponse.records as AnyObject
    case 201:
        // 解析成订单实体(示例)
        let fullResponse = try JSONDecoder().decode(Response<Order>.self, from: data)
        finalResponse.records = fullResponse.records as AnyObject
    default:
        // 处理其他状态,比如records为空或其他类型
        finalResponse.records = nil
    }
    
    // 现在finalResponse就是你需要的完整实体了
} catch {
    // 处理解析错误
    print("解析失败:\(error.localizedDescription)")
}

// 配合使用的泛型响应结构体
struct Response<T: Codable>: Codable {
    let status: Int?
    let statusDescription: String?
    let records: T?
}

方案二:自定义Codable实现(适合需要基类直接支持Codable的场景)

如果你希望entStatusAndDescription本身直接实现Codable,可以通过自定义解码逻辑,先保存records的原始JSON数据,之后再转成具体类型:

public class entStatusAndDescription: NSObject, Codable {
    var status: Int?
    var statusDescription: String?
    var records: AnyObject? // 这里保存原始JSON数据或解析后的实体
    
    enum CodingKeys: String, CodingKey {
        case status, statusDescription, records
    }
    
    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 解析基础状态字段
        status = try container.decodeIfPresent(Int.self, forKey: .status)
        statusDescription = try container.decodeIfPresent(String.self, forKey: .statusDescription)
        
        // 保存records的原始JSON数据(后续可转成具体类型)
        if let recordsRaw = try container.decodeIfPresent(AnyCodable.self, forKey: .records) {
            records = recordsRaw.value as AnyObject
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(status, forKey: .status)
        try container.encodeIfPresent(statusDescription, forKey: .statusDescription)
        
        // 编码records字段
        if let recordsObj = records {
            try container.encode(AnyCodable(recordsObj), forKey: .records)
        }
    }
}

// 自定义AnyCodable类型,用于兼容任意JSON数据
struct AnyCodable: Codable {
    let value: Any
    
    init(_ value: Any) {
        self.value = value
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        if let intVal = try? container.decode(Int.self) {
            value = intVal
        } else if let stringVal = try? container.decode(String.self) {
            value = stringVal
        } else if let boolVal = try? container.decode(Bool.self) {
            value = boolVal
        } else if let doubleVal = try? container.decode(Double.self) {
            value = doubleVal
        } else if let arrayVal = try? container.decode([AnyCodable].self) {
            value = arrayVal.map { $0.value }
        } else if let dictVal = try? container.decode([String: AnyCodable].self) {
            value = dictVal.mapValues { $0.value }
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "无法解析成Any类型")
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        
        switch value {
        case let intVal as Int:
            try container.encode(intVal)
        case let stringVal as String:
            try container.encode(stringVal)
        case let boolVal as Bool:
            try container.encode(boolVal)
        case let doubleVal as Double:
            try container.encode(doubleVal)
        case let arrayVal as [Any]:
            try container.encode(arrayVal.map(AnyCodable.init))
        case let dictVal as [String: Any]:
            try container.encode(dictVal.mapValues(AnyCodable.init))
        default:
            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: container.codingPath, debugDescription: "无法编码该类型"))
        }
    }
}

使用这种方案时,你可以在解析完entStatusAndDescription后,根据status把records转成具体实体:

if let status = finalResponse.status, status == 200, let recordsDict = finalResponse.records as? [String: Any] {
    let recordsData = try JSONSerialization.data(withJSONObject: recordsDict, options: [])
    let customer = try JSONDecoder().decode(Customer.self, from: recordsData)
    // 现在customer就是解析后的客户实体了
}

小提示

  • 如果不需要Objective-C兼容,建议把entStatusAndDescription改成Swift的struct,更轻量且符合Swift的设计习惯。
  • 方案一中的泛型Response结构体可以复用在所有API请求中,非常方便。

内容的提问来源于stack exchange,提问作者Shravan Agrawal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:37:39