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

Swift 5.7中如何正确编解码[any Content]异构数组?

解决Swift异构Codable数组的编码/解码问题

核心思路

处理[any Content]这类异构Codable数组的关键,是通过ContentType枚举标记每个元素的类型:编码时为每个元素附加类型标识,解码时先读取类型再对应解析元素内容,从根本避免循环覆盖数组的问题。

完整代码实现

1. 定义基础协议与类型枚举

import Foundation

// 遵循Codable的Content协议,要求实现类提供对应的ContentType
protocol Content: Codable {
    static var contentType: ContentType { get }
}

// 标记内容类型的枚举,需遵循Codable
enum ContentType: String, Codable {
    case text, image, video
}

// 示例Content实现类
struct TextContent: Content {
    static let contentType: ContentType = .text
    let text: String
}

struct ImageContent: Content {
    static let contentType: ContentType = .image
    let url: URL
    let width: Int
    let height: Int
}

struct VideoContent: Content {
    static let contentType: ContentType = .video
    let url: URL
    let duration: TimeInterval
}

2. ContentCollection的编码/解码逻辑

class ContentCollection: Codable {
    var contents: [any Content]
    
    init(contents: [any Content]) {
        self.contents = contents
    }
    
    // 自定义编码:逐个元素添加到JSON数组
    func encode(to encoder: Encoder) throws {
        var rootContainer = encoder.container(keyedBy: CodingKeys.self)
        var contentsArray = rootContainer.nestedUnkeyedContainer(forKey: .contents)
        
        for content in contents {
            // 为每个元素创建单独的键值容器,存储类型标识和内容
            var elementContainer = contentsArray.nestedContainer(keyedBy: ElementCodingKeys.self)
            try elementContainer.encode(type(of: content).contentType, forKey: .type)
            try content.encode(to: elementContainer.superEncoder(forKey: .content))
        }
    }
    
    // 自定义解码:根据类型标识解析对应元素
    required init(from decoder: Decoder) throws {
        let rootContainer = try decoder.container(keyedBy: CodingKeys.self)
        var contentsArray = try rootContainer.nestedUnkeyedContainer(forKey: .contents)
        var decodedContents = [any Content]()
        
        while !contentsArray.isAtEnd {
            let elementContainer = try contentsArray.nestedContainer(keyedBy: ElementCodingKeys.self)
            let contentType = try elementContainer.decode(ContentType.self, forKey: .type)
            let contentDecoder = try elementContainer.superDecoder(forKey: .content)
            
            // 根据类型匹配对应的Content实现类
            switch contentType {
            case .text:
                decodedContents.append(try TextContent(from: contentDecoder))
            case .image:
                decodedContents.append(try ImageContent(from: contentDecoder))
            case .video:
                decodedContents.append(try VideoContent(from: contentDecoder))
            }
        }
        
        self.contents = decodedContents
    }
    
    // 根层级编码键
    enum CodingKeys: String, CodingKey {
        case contents
    }
    
    // 单个元素的编码键
    enum ElementCodingKeys: String, CodingKey {
        case type, content
    }
}

3. 使用示例

// 创建异构Content数组
let sampleContents: [any Content] = [
    TextContent(text: "Hello Swift"),
    ImageContent(url: URL(string: "https://example.com/photo.jpg")!, width: 1080, height: 1920),
    VideoContent(url: URL(string: "https://example.com/clips.mp4")!, duration: 90.5)
]

let collection = ContentCollection(contents: sampleContents)

// 编码测试
do {
    let jsonData = try JSONEncoder().encode(collection)
    if let jsonString = String(data: jsonData, encoding: .utf8) {
        print("编码结果:\n\(jsonString)")
    }
} catch {
    print("编码失败: \(error.localizedDescription)")
}

// 解码测试
do {
    let jsonData = try JSONEncoder().encode(collection)
    let decodedCollection = try JSONDecoder().decode(ContentCollection.self, from: jsonData)
    print("\n解码成功,共\(decodedCollection.contents.count)个元素")
} catch {
    print("解码失败: \(error.localizedDescription)")
}

关键说明

  • 编码时:通过nestedUnkeyedContainer创建数组容器,每个元素单独用nestedContainer存储type和content字段,确保元素逐个添加不覆盖。
  • 解码时:先读取每个元素的类型标识,再匹配对应的Content实现类解析内容,保证异构数组的正确还原。
  • 扩展性:新增Content类型时,只需在ContentType中添加对应的case,并在解码的switch分支中新增处理逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:10:32