假设我从后端返回了这样的数据结构:
struct DummyItem: Decodable {
let number: Int
let name: String
}
struct DummyResponse {
let items: [DummyItem]
let meta: String
}
我也有这个helper 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
结构的catch
块中完成。这似乎是一项简单的任务(解码器必须以某种方式访问它试图解码的原始数据,对吗?)
2条答案
按热度按时间2sbarzqh1#
Decoder
包含一个userInfo
字典,你可以把任何你喜欢的,它将在解码过程中访问。在解码过程中,您还可以访问当前编码键数组(decoder.codingPath
)(例如["items", "number"]
(如果正在解码,则为“数字”)。因此,您可以将JSON数据存储在
userInfo
中,并与编码路径沿着获得损坏的JSON数据。下面的示例应该可以按照您的预期工作。
以上将打印
ia2d9nvy2#
正如所描述的,这是不可能的。JSONDecoder内部不对Data进行操作。它在NSDictionary(由JSONSerialization生成)或
JSONValue
枚举上运行,具体取决于平台。(在新的swift-foundation中,它稍微复杂一点,但重点是相同的。)当它调用init(from:)
时,底层的Data不再可用。要做你想做的事情,你需要手动解析JSON。这并不像听起来那么难,我做了一点,包括实现类似于你所描述的事情。但它确实需要从stdlib中复制大量代码,以使内部类型成为公共类型。
考虑到你似乎只关心“意外的JSON”,而不是实际损坏的JSON(即不是有效的JSON),那么我通常更喜欢的技术是将东西解析成像JSONValue这样的结构体(这是我用一堆帮助函数构建的自定义结构体,以使其更好地使用),然后用
init(json: JSONValue) throws
替换Decodable
。这些初始化器往往非常容易编写,并且非常容易通过错误处理等进行扩展。我没有特别地将它捆绑到一个可重用的包中。我倾向于把这些东西复制到我的项目中,并使其适应当地的问题。如果您使用JSONDecoder的stdlib JSONValue实现(上面链接),那么您还可以使JSONDecoderImpl公开,然后您可以在需要时自动从JSONValues解码Decodables。
所有这些问题都没有简单的答案,但是如果它对您的项目很重要,那么它肯定是非常可行的。JSONValue代码很乏味,但并不复杂(与NSDictionary代码相比,这是一个真正的痛苦)。
我在我的Advanced Codable talk中涉及了其中的一些内容,并讨论了类似的主题,这可能会很有用。