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

如何在Swift解码失败时获取问题对象的原始JSON数据?

解码失败时获取损坏对象的原始JSON数据

问题背景

现有Swift解码逻辑中,通过OptionalObject结构体跳过数组中解码失败的元素,但需要捕获这些损坏元素的原始JSON数据用于问题排查。相关代码与场景如下:

现有结构与解码逻辑

后端返回数据结构:

struct DummyItem: Decodable {
    let number: Int
    let name: String
}

struct DummyResponse {
    let items: [DummyItem]
    let meta: String
}

用于跳过失败元素的辅助结构体:

struct OptionalObject<Base: Decodable>: Decodable {
    let value: Base?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self.value = try container.decode(Base.self)
        } catch {
            self.value = nil
        }
    }
}

响应解码实现:

extension DummyResponse: Decodable {
    enum CodingKeys: String, CodingKey {
        case items, meta
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let nullableItems = try container.decode([OptionalObject<DummyItem>].self, forKey: .items)
        self.items = nullableItems.compactMap({ $0.value })
        self.meta = try container.decode(String.self, forKey: .meta)
    }
}

当下游返回包含损坏元素的JSON时(如下示例),现有逻辑会跳过损坏项,但无法获取其原始JSON:

{
    "items": [
        {
            "number": 1,
            "name": "first"
        },
        {
            "number": 2,
            "name": "second"
        },
        {
            "number": 3,
            "name": null
        }
    ],
    "meta": "Metadata"
}

解决方案

修改OptionalObject结构体,新增存储原始JSON的属性,并在解码失败的catch块中捕获当前元素的原始JSON数据。需借助通用的AnyCodable结构体处理任意类型的JSON元素:

1. 实现AnyCodable结构体

用于解码任意JSON类型,为后续序列化为原始数据提供支持:

struct AnyCodable: Decodable {
    let value: Any

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        if let intValue = try? container.decode(Int.self) {
            value = intValue
        } else if let doubleValue = try? container.decode(Double.self) {
            value = doubleValue
        } else if let stringValue = try? container.decode(String.self) {
            value = stringValue
        } else if let boolValue = try? container.decode(Bool.self) {
            value = boolValue
        } else if let arrayValue = try? container.decode([AnyCodable].self) {
            value = arrayValue.map { $0.value }
        } else if let dictValue = try? container.decode([String: AnyCodable].self) {
            value = dictValue.mapValues { $0.value }
        } else if container.decodeNil() {
            value = NSNull()
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "无法解码为任意JSON类型")
        }
    }
}

2. 修改OptionalObject结构体

新增rawJSON属性存储损坏元素的原始JSON数据,并在catch块中完成捕获:

struct OptionalObject<Base: Decodable>: Decodable {
    let value: Base?
    let rawJSON: Data? // 存储损坏元素的原始JSON二进制数据

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        do {
            // 解码成功时,value为元素实例,rawJSON置空
            self.value = try container.decode(Base.self)
            self.rawJSON = nil
        } catch {
            // 解码失败时,用AnyCodable解码当前元素,再序列化为Data
            let anyCodable = try container.decode(AnyCodable.self)
            self.rawJSON = try JSONSerialization.data(withJSONObject: anyCodable.value)
            self.value = nil
        }
    }
}

3. 提取损坏元素的原始JSON

在DummyResponse的解码逻辑中,可从nullableItems中筛选出损坏元素的原始JSON:

extension DummyResponse: Decodable {
    enum CodingKeys: String, CodingKey {
        case items, meta
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let nullableItems = try container.decode([OptionalObject<DummyItem>].self, forKey: .items)
        self.items = nullableItems.compactMap({ $0.value })
        
        // 提取并处理损坏元素的原始JSON(转换为字符串示例)
        let corruptedItems = nullableItems
            .compactMap { item -> String? in
                guard let rawData = item.rawJSON else { return nil }
                return String(data: rawData, encoding: .utf8)
            }
        
        // 可根据需求打印或上报损坏元素
        print("检测到损坏的元素:\(corruptedItems)")
        
        self.meta = try container.decode(String.self, forKey: .meta)
    }
}

说明

  • AnyCodable支持解码所有标准JSON类型(数字、字符串、布尔值、数组、字典、null),确保能捕获任意格式的损坏元素。
  • 捕获的rawJSON为Data类型,可根据需求转换为字符串或直接用于序列化操作。
  • 该方案在不影响正常元素解码的前提下,完整保留了损坏元素的原始数据,便于后续问题排查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 10:35:35