JSONDecoder 无法处理空响应

Posted

技术标签:

【中文标题】JSONDecoder 无法处理空响应【英文标题】:JSONDecoder can't handle null response 【发布时间】:2019-01-10 18:42:08 【问题描述】:

上下文

我正在使用Firebase Database REST API 和 JSONDecoder / JSONEncoder。到目前为止,它运行良好。 但是对于removing data,预期的返回响应是null,而 JSONDecoder 似乎不太喜欢这样。

这是我通过 Postman 发送的查询类型以及我返回的内容(敏感数据除外)。

DELETE /somedata/-LC03I3oHcLhQ/members/ZnWsJtrZ5UfFS6agajbL2hFlIfG2.json
content-type: application/json
cache-control: no-cache
postman-token: ab722e0e-98ed-aaaa-bbbb-123f64696123
user-agent: PostmanRuntime/7.2.0
accept: */*
host: someapp.firebaseio.com
accept-encoding: gzip, deflate
content-length: 39


HTTP/1.1 200
status: 200
server: nginx
date: Thu, 02 Aug 2018 21:53:27 GMT
content-type: application/json; charset=utf-8
content-length: 4
connection: keep-alive
access-control-allow-origin: *
cache-control: no-cache
strict-transport-security: max-age=31556926; includeSubDomains; preload

null

如您所见,响应代码为200,正文为null

错误

当我收到响应时,这是我得到的错误:

Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "给定的数据不是有效的 JSON。", 基础错误:可选(错误域=NSCocoaErrorDomain 代码=3840 “JSON 文本没有以数组或对象开头,并且选项允许 片段未设置。” UserInfo=NSDebugDescription=JSON 文本未设置 从数组或对象和允许未设置片段的选项开始。))))

我尝试创建一个自定义类型 (NoReply) 以按照 a previous post 处理此问题,但无济于事。

代码

这是发生错误的地方:

        resource: 
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            return try decoder.decode(Resource.self, from: $0)
        ,
        error: 
            let decoder = JSONDecoder()
            return try decoder.decode(FirebaseError.self, from: $0)
        

显然,即使我提供自定义 NoReply 类型(根据上面提到的帖子)JSONDecoder 也不喜欢 null

有什么建议吗?


作为旁注,这是他们的文档中关于 DELETE 操作响应的内容:

成功的 DELETE 请求由 200 OK HTTP 状态代码指示 响应包含 JSON null

【问题讨论】:

听起来你应该使用文档 - 检查响应中的“null”。 您是否尝试过将***别放入带有 case .data、.null 的枚举中,并使用 singleValueContainer 手动将其解码为 String?,如果失败则正常解码并返回 .data(myStructure) . @DougStevenson,是的,当然:)。不幸的是,JSONDecoder 在让您访问底层数据之前会失败。 @Purpose,是的,我尝试使用decoder.unkeyedContainer() 或decoder.singleValueContainer() 来实现我自己的`init(来自解码器:解码器)`。但它的失败点其实在这之前。我可以尝试在客户端中捕获返回的raw 数据,但我希望有一个更好的解决方案。谢谢! @Nick 你试过解码作为可选吗? 【参考方案1】:

不幸的是,JSONDecoder 没有公开底层的 JSONSerialization 选项 (.allowFragments),该选项将支持 JSON 片段,例如单独的 null 值。您可以尝试转换响应或直接使用JSONSerialization。不幸的是,这里没有什么优雅可做的。

【讨论】:

据说这在 forums.swift.org/t/allowing-top-level-fragments-in-jsondecoder/… GitHub 链接中已更改:github.com/apple/swift/pull/30615/… 尽管响应为空,但仍然出现错误:(【参考方案2】:

快速跟进

在几次成功的 hack 之后,我想出的最优雅的解决方案是:

使用NoReply(如Zoul所述) 将null 字符串转换为空的JSON 对象结构()

首先,转换:

            if "null" == String(data: data, encoding: .utf8) 
                let json = Data("".utf8)

然后反馈给处理请求响应的闭包:

        resource: 
            let decoder = JSONDecoder()
            return try decoder.decode(Resource.self, from: $0)
        ,

资源就是:

公共结构 NoReply:可解码

现在效果很好,允许我处理不存在的对象上的 DELETE 案例和 GET 案例,它返回 null

感谢您的帮助!

【讨论】:

提示:您可以使用Data(string.utf8)String 转换为Data @JonShier :用你的建议更新了帖子。谢谢。 @Nick,你如何处理任何键的空值?假设,JSON 是 "somekey":null 。 .这里 somekey 是一个字符串?可选类型。 @MilanKamilya,我只遇到了 Firebase API 返回的空片段问题。对于特定键的null 值,我只是使用了我的可解码数据模型的try container.decodeIfPresent,它会自动为我执行此操作(假设您正在解码的属性是可选的)。 @Nick,我也将该属性标记为可选,但它从 JSONDecoder 发送 ValueNotFound 错误。通过标记可选,它确保不会发生 KeyNotFound 错误。因此,在输入 JSONDecoder 之前,我必须删除所有 null 和相应的属性。

以上是关于JSONDecoder 无法处理空响应的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jsonDecoder 处理来自 JSON 响应的动态键?

快速处理两个不同的http json响应

JSONDecoder() 仅在 Swift 中处理 Null 值

使用 JSONDecoder 的数组与字典响应结构

如何准备 API 响应以在 swift 中与 jsonDecoder 一起使用

如何使用 JSONDecoder 根据订单键获取排序数据?