使用 Swift 4 的 Decodable 解码 Void

Posted

技术标签:

【中文标题】使用 Swift 4 的 Decodable 解码 Void【英文标题】:Decoding Void with Swift 4’s Decodable 【发布时间】:2018-01-19 23:03:20 【问题描述】:

我有一个通用的 REST 请求:

struct Request<T> …

T是请求的返回类型,例如:

struct Animal …
let animalRequest = Request<Animal>
let animal: Animal = sendRequest(animalRequest)

现在我想说泛型类型必须符合Decodable,这样我才能解码来自服务器的 JSON 响应:

struct Request<T> where T: Decodable …
struct Animal: Decodable …

这是有道理且有效的——直到我收到一个没有响应的请求,Request&lt;Void&gt;。编译器对此不满意:

Type 'Void' does not conform to protocol 'Decodable'

编译器很快发现了我通过将Decodable 一致性添加到Void 来解决这个问题的恶作剧:

extension Void: Decodable … // Error: Non-nominal type 'Void' cannot be extended

让请求泛型而不是返回类型感觉是正确的。有没有办法让它与Void 返回类型一起工作? (例如,只是在服务器上创建一些东西而不返回任何东西的请求。)

【问题讨论】:

也许我误解了这个问题,但这取决于你 - 开发者 - 来处理 avoiding Void 请求跨度> 我能理解你的观点,但同时感觉如果一个事物在 x 上是通用的,那么 Void 又名零元组 ()应该是 x 的有效值。毕竟EquatableDecodable 是微不足道的。 @zoul 仍然想知道Request&lt;Void&gt; 应该是什么意思。你为什么要使用这样的东西?如果那是响应类型,则永远不会是 Void。它可以是空的,但绝不是Void empty 和Void 有什么区别?对我来说,在普通函数中有一个完美的类比,返回 Void 的请求与返回 Void 的函数相同。两者都只用于副作用。 【参考方案1】:

我发现有时其他类型的其他编码对象可以解码为 NoReply.self。例如自定义错误类型(枚举)即可。

本案例的游乐场示例:

enum MyError: String, Codable 
    case general


let voidInstance = VoidResult()
let errorInstance = MyError.general
let data1 = try! JSONEncoder().encode(voidInstance)
let data2 = try! JSONEncoder().encode(errorInstance)

let voidInstanceDecoded = try! JSONDecoder().decode(VoidResult.self, from: data1)
//VoidResult as expected

let errorInstanceDecoded = try! JSONDecoder().decode(MyError.self, from: data2)
//MyError.general as expected

let voidInstanceDecodedFromError = try! JSONDecoder().decode(VoidResult.self, from: data2)
//VoidResult - NOT EXPECTED

let errorInstanceDecodedFromVoid = try! JSONDecoder().decode(ScreenError.self, from: data1)
//DecodingError.typeMismatch - Expected

所以我的建议是添加“NoReply 的唯一性(zoul 的回答)):

struct VoidResult: Codable 
    var id = UUID()


let voidInstanceDecodedFromError = try! JSONDecoder().decode(VoidResult.self, from: data2)
//DecodingError.typeMismatch - Now its fine - as expected

【讨论】:

【参考方案2】:

一个简单的解决方法是引入一个自定义的“no-reply”类型来替换Void

struct NoReply: Decodable 

VoidDecodable 保持一致是不可能的。 Void 只是空元组() 的类型别名,元组目前不能符合协议,但它们最终会。

【讨论】:

这个解决方案很好 太棒了。谢谢! 这对我不起作用,我得到一个 DecodingError.dataCorrupted with context: Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo=NSDebugDescription=No value.)) @Marmoy 也许您正试图解码一个空数据。 JSONDecoder 要求输入数据是有效的 JSON 文档。作为一种解决方法,您可以检测响应数据是否为空,在这种情况下,将其替换为模拟的空 json 数据:let emptyJson = "".data(using: .utf8)

以上是关于使用 Swift 4 的 Decodable 解码 Void的主要内容,如果未能解决你的问题,请参考以下文章

Swift 4 JSON Decodable 解码类型更改的最简单方法

在 Swift 4 中使用 Decodable 和继承

使用 Swift 4 Decodable 将字符串 JSON 响应转换为布尔值

Swift 4 Decodable - 没有与键 CodingKeys 关联的值 [重复]

Swift Decodable - 如何解码经过 base64 编码的嵌套 JSON

使用协议的 Swift 通用解码器