假设我有以下从后端返回的数据 struct :

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

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

我还有这个助手 struct 来跳过解码失败的对象:

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)
    }
}

例如,当我在后端响应中收到一些损坏的DummyItem时,我仍将获得DummyResponse,但该项目将被跳过

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

发生这种情况时,我如何访问有问题对象的原始JSON数据? 我假设这可以在OptionalObject struct 的catch块中完成.这似乎是一项简单的任务(解码器必须以某种方式访问它试图解码的原始数据,对吗?)

推荐答案

正如所描述的,这是不可能的.JSONDecoder不在内部操作数据.它可以在NSDictionary (produced by JSONSerialization)JSONValue enum上运行,具体取决于平台.(在新的Swift-Foundation中,是a little more complicated,但重点是一样的.)当它呼叫init(from:)时,基础数据不再可用.

要执行您想要的操作,您需要手动解析JSON.这并不像听起来那么难,我做了一点,包括实现你所描述的类似的事情.但它往往需要从stdlib中复制大量代码,以使内部类型成为公共类型.

假设您似乎只关心"意外的JSON",而不是实际损坏的JSON(即无效的JSON),那么我通常更喜欢的技术是将事物解析成类似JSONValue的 struct (这是我用一组助手函数构建的定制 struct ,以使其更易于使用),然后用init(json: JSONValue) throws替换Decodable.这些初始化器往往很容易编写,并且很容易通过错误处理等进行扩展.我还没有特别地将其Bundle 到可重用的包中.我倾向于将这些东西复制到我的项目中,并根据当地的问题进行调整.

如果您停止JSONDecoder的stdlib JSONValue实现(上面链接),那么您还可以将JSONDecoderImpl设置为公共的,然后您可以根据需要自动解码JSONValue中的Decodable.

所有这些都没有微不足道的答案,但如果它对您的项目很重要,那么它肯定都是非常可行的.JSONValue代码很繁琐,但并不是真的很复杂(与NSDictionary代码相比,这是一种真正的痛苦).

我谈到了其中的一些,并在我的Advanced Codable talk岁时讨论了类似的主题,这可能是有用的.

Swift相关问答推荐

如何在init()中使用setter/getters?

如何为任务扩展的泛型静态函数获得自动类型推理

无法创建MKAssociateRegion对象

将matchedGeometryEffect 应用于列表视图内的视图时,列表视图中的项目会受到影响 - SwiftUI

SwiftUI,如何更改具有多个按钮和一个布尔条件的一个按钮标签

如何判断设备是否为 Vision Pro - Xcode 15 Beta 4

如何从我的 macOS 应用程序打开 (.log) 文件?

在 Swift 的异步上下文中,如何指示我想要函数的同步版本?

swift 是否遇到 Java 的 2gb 最大序列化大小问题?

为什么 String.contains 在我导入 Foundation 时表现不同?

Swift 根据总值将数组拆分为块

如何让 UIKit 挤压你的视图,使其适合屏幕

Xcode 7.3 Swift 的语法高亮和代码完成问题

如何从 Button 获取标签名称?

如何为多个类 Swift 进行扩展

'#selector' 的参数不引用 '@objc' 方法、属性或初始化程序

如何在 Swift 中将 UILabel 居中?

是 swift 中另一个日期的同一周、月、年的日期

在 Swiftui 中是否有一种简单的方法可以通过捏合来放大图像?

如何判断 firebase 数据库值是否存在?