使用Swift 4的可解码协议(Decodable)解析Void

29

我有一个通用的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<Void>。编译器对此不太满意:

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

我的调皮尝试通过为Void添加Decodable符合性来解决这个问题,但编译器很快就发现了它的问题。
extension Void: Decodable {…} // Error: Non-nominal type 'Void' cannot be extended

请求的返回类型应该是通用的,这种做法是正确的。是否有一种方法可以使其与Void返回类型一起工作?(例如仅在服务器上创建某些内容且无需返回任何内容的请求。)


也许我误解了问题,但是避免“Void”请求是由您作为开发人员来处理的。 - vadian
我能理解你的观点,但同时感觉如果一件事情是针对_x_通用的,那么Void,也就是零元组(), 应该是_x_的有效值。毕竟它是微不足道的EquatableDecodable - zoul
@zoul 仍然不明白 Request<Void> 意味着什么。为什么要使用这样的东西?如果这是一个响应类型,它从来不会是 Void。它可以为空,但从来不是 Void - Sulthan
1
空和Void之间有什么区别?对我来说,在普通函数中有一个很好的类比,返回Void的请求就像返回Void的函数一样。两者都仅用于副作用。 - zoul
2个回答

42

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

struct NoReply: Decodable {}

无法将 Void 转换为 DecodableVoid 只是一个空元组的类型别名,(),而元组目前无法符合协议,但最终会支持。


这个解决方案很好。 - iWheelBuy
惊人的。谢谢! - Jan
2
这对我不起作用,我得到了一个DecodingError.dataCorrupted错误,上下文为:Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.})) - Marmoy
6
也许你正在尝试解码空数据。JSONDecoder要求输入的数据必须是有效的JSON文档。作为解决方法,你可以检测响应数据是否为空,如果是,则用模拟的空JSON数据替换它: let emptyJson = "{}".data(using: .utf8) - Zimes

1
我发现有时候其他类型的编码对象可以被解码为NoReply.self。例如,自定义错误类型(枚举)可以这样做。
此案例的Playground示例:
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

所以我的建议是添加“不重复性到无回复(zoul的答案):”。
struct VoidResult: Codable {
    var id = UUID()
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接