可解码返回对象
Decodable returning object
我有一个可解码的 class:
struct AuthenticationResponse : Decodable {
var status: String
var error: Error
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = "
}
struct Error : Decodable {
var desc: String
var code: String
}
在错误 class 我有:
要解码为 class,我有:
URLSession.shared.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
if let jsonData = data{
let decoder = JSONDecoder()
print("hey")
print("response: \(String(data:jsonData, encoding:.utf8))")
completion(try! decoder.decode(AuthenticationResponse.self, from: jsonData))
}
}.resume()
因为我收到的一些回复是(成功回复):
{
“status”: “SUCCESS” “error”: null, "access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1 N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600, "token_type": "bearer", "scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2N mJjYmIxMjNkMjE1NWFhNWY0Mg"
}
然后失败的响应只包含一个错误对象,其中包含 desc 和代码。
我想要实现的是一个可解码的 class 适用于两种情况(响应成功和失败时),但我不确定如何实现。我知道我可以制作 2 个单独的可解码 classes 但这会使事情变得更加混乱,因为我必须确定响应是否错误并填充到 return 不同的 classes .
有谁知道我应该如何实现这个>
我会给它一个try
,但首先我们需要理清我认为有点粗制滥造的问题。由于 Error
是(著名且广泛使用的)protocol
的名称,因此应该重命名它,并且由于您希望能够在 AuthenticationResponse
中将其留空,因此显然它必须是可选的那里(问题是为什么它在 Response
中,但我将把它放在一边)。这给我们留下了以下内容:
struct AuthError : Decodable {
var desc: String
var code: String
}
struct AuthenticationResponse : Decodable {
var status: String
var error: AuthError?
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = ""
}
然后我们需要有关两个相关案例的一些示例数据,我使用了:
let okData = """
{
"status": "SUCCESS",
"error": null,
"access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2NmJjYmIxMjNkMjE1NWFhNWY0Mg"
}
""".data(using: .utf8)!
let errData = """
{
"desc": "username or password incorrect",
"code": "404"
}
""".data(using: .utf8)!
现在我们可以定义一个 enum
return 类型,它允许我们所有的情况:
enum AuthResult {
case ok(response: AuthenticationResponse)
case authError(error: AuthError)
case parseError(description: String)
case fatal
}
这最终允许我们为接收到的身份验证数据编写 parse
函数:
func parse(_ jsonData:Data) -> AuthResult {
let decoder = JSONDecoder()
do {
let authRes = try decoder.decode(AuthenticationResponse.self, from: jsonData)
return .ok(response: authRes)
} catch {
do {
let errRes = try decoder.decode(AuthError.self, from: jsonData)
return .authError(error: errRes)
} catch let errDecode {
return .parseError(description: errDecode.localizedDescription)
}
}
}
Playground 中的所有这些都将允许使用
switch parse(okData) {
case let .ok(response):
print(response)
case let .authError(error):
print(error)
case let .parseError(description):
print("You threw some garbage at me and I was only able to \(description)")
default:
print("don't know what to do here")
}
与您在大多数其他语言中造成的混乱相比,这仍然是优雅的,但是如果将 AuthenticationResponse
定义为(常规)[= parse
函数的 51=] 类型,并通过 throw
ing 一些 enum
(符合 Error
)和一些合适的有效负载来提供其余部分。
(主要)来自 Java 我仍然避免使用异常作为 "somewhat" 常规控制流(如 "regular" 登录失败),但考虑到 Swifts 更合理的方法对于例外情况,这可能需要重新考虑。
无论如何,这为您提供了一个函数来解析您的服务回复的任何一种情况,以及以 "uniform" 方式处理它们的体面方式。由于您可能无法修改处理您的 request
的服务的行为,这可能是唯一可行的选择。但是,如果您能够修改服务,您应该努力获得一个 "uniform" 可以通过一次调用 JSONDecoder.decode
来解析的回复。你仍然需要解释可选值(就像你在上面的例子中应该做的那样,因为使用它们仍然很痛苦,即使 Swifts 出色的编译器支持迫使你 "do the right thing"),但它会减少你的解析容易出错。
我有一个可解码的 class:
struct AuthenticationResponse : Decodable {
var status: String
var error: Error
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = "
}
struct Error : Decodable {
var desc: String
var code: String
}
在错误 class 我有:
要解码为 class,我有:
URLSession.shared.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
if let jsonData = data{
let decoder = JSONDecoder()
print("hey")
print("response: \(String(data:jsonData, encoding:.utf8))")
completion(try! decoder.decode(AuthenticationResponse.self, from: jsonData))
}
}.resume()
因为我收到的一些回复是(成功回复):
{
“status”: “SUCCESS” “error”: null, "access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1 N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600, "token_type": "bearer", "scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2N mJjYmIxMjNkMjE1NWFhNWY0Mg"
}
然后失败的响应只包含一个错误对象,其中包含 desc 和代码。
我想要实现的是一个可解码的 class 适用于两种情况(响应成功和失败时),但我不确定如何实现。我知道我可以制作 2 个单独的可解码 classes 但这会使事情变得更加混乱,因为我必须确定响应是否错误并填充到 return 不同的 classes .
有谁知道我应该如何实现这个>
我会给它一个try
,但首先我们需要理清我认为有点粗制滥造的问题。由于 Error
是(著名且广泛使用的)protocol
的名称,因此应该重命名它,并且由于您希望能够在 AuthenticationResponse
中将其留空,因此显然它必须是可选的那里(问题是为什么它在 Response
中,但我将把它放在一边)。这给我们留下了以下内容:
struct AuthError : Decodable {
var desc: String
var code: String
}
struct AuthenticationResponse : Decodable {
var status: String
var error: AuthError?
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = ""
}
然后我们需要有关两个相关案例的一些示例数据,我使用了:
let okData = """
{
"status": "SUCCESS",
"error": null,
"access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2NmJjYmIxMjNkMjE1NWFhNWY0Mg"
}
""".data(using: .utf8)!
let errData = """
{
"desc": "username or password incorrect",
"code": "404"
}
""".data(using: .utf8)!
现在我们可以定义一个 enum
return 类型,它允许我们所有的情况:
enum AuthResult {
case ok(response: AuthenticationResponse)
case authError(error: AuthError)
case parseError(description: String)
case fatal
}
这最终允许我们为接收到的身份验证数据编写 parse
函数:
func parse(_ jsonData:Data) -> AuthResult {
let decoder = JSONDecoder()
do {
let authRes = try decoder.decode(AuthenticationResponse.self, from: jsonData)
return .ok(response: authRes)
} catch {
do {
let errRes = try decoder.decode(AuthError.self, from: jsonData)
return .authError(error: errRes)
} catch let errDecode {
return .parseError(description: errDecode.localizedDescription)
}
}
}
Playground 中的所有这些都将允许使用
switch parse(okData) {
case let .ok(response):
print(response)
case let .authError(error):
print(error)
case let .parseError(description):
print("You threw some garbage at me and I was only able to \(description)")
default:
print("don't know what to do here")
}
与您在大多数其他语言中造成的混乱相比,这仍然是优雅的,但是如果将 AuthenticationResponse
定义为(常规)[= parse
函数的 51=] 类型,并通过 throw
ing 一些 enum
(符合 Error
)和一些合适的有效负载来提供其余部分。
(主要)来自 Java 我仍然避免使用异常作为 "somewhat" 常规控制流(如 "regular" 登录失败),但考虑到 Swifts 更合理的方法对于例外情况,这可能需要重新考虑。
无论如何,这为您提供了一个函数来解析您的服务回复的任何一种情况,以及以 "uniform" 方式处理它们的体面方式。由于您可能无法修改处理您的 request
的服务的行为,这可能是唯一可行的选择。但是,如果您能够修改服务,您应该努力获得一个 "uniform" 可以通过一次调用 JSONDecoder.decode
来解析的回复。你仍然需要解释可选值(就像你在上面的例子中应该做的那样,因为使用它们仍然很痛苦,即使 Swifts 出色的编译器支持迫使你 "do the right thing"),但它会减少你的解析容易出错。