【问题标题】:Decodable returning object可解码返回对象
【发布时间】:2018-06-01 18:41:45
【问题描述】:

我有一个可解码的类:

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
}

在我的错误类中:

要解码到这个类,我有:

 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 和代码。

我想要实现的是一个适用于两种情况的可解码类(当响应成功和失败时)但是我不知道如何实现这一点。我知道我可以制作 2 个单独的可解码类,但这会使事情变得更加混乱,因为我必须确定响应是否是错误并填充以返回不同的类。

有谁知道我应该如何做到这一点>

【问题讨论】:

  • 不幸的是,您根本不清楚您要做什么。在您的示例中,您已经有两个可解码的类,您想按原样使用它们吗?在这种情况下,如果解码失败或类似情况,您可能必须创建自己的 AuthenticationResponse-object,其中包含 Error
  • 如果你想获得响应的可能性,你应该定义一些Response协议,但你可能已经知道了。请更具体地说明您预期解决方案的所需属性。
  • 我基本上想要一个允许将 JSON 解码为它的类。 AuthenticationResponse 有效,但是当没有返回错误时(成功响应)但是当返回错误时(失败响应)我得到一个错误,这是因为从失败响应返回的 JSON 包含最少的数据并且显然没有正确映射到 AuthenticateResponse 正确@Patru
  • 如果您的问题是返回错误,那么您应该不惜一切代价发布产生错误的JSON,并尽可能具体地说明您收到的错误。可能JSONDecoder 不会对他得到的东西感到满意,您将不得不在某些点上添加选项。

标签: swift api decodable


【解决方案1】:

我会给它一个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 返回类型,它允许我们所有的情况:

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)
        }
    }
}

游乐场中的所有这些都将允许使用

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 定义为@987654334 的(常规)返回类型是否更有意义@函数并通过throw提供一些enum(符合Error)和一些合适的有效载荷。

(主要)来自 Java,我仍然避免将异常用作“某种程度”的常规控制流(如在“常规”登录失败中),但鉴于 Swift 对异常采取更合理的方法,这可能需要重新考虑。

无论如何,这为您提供了一个功能来解析您的服务回复的任何一种情况,并以一种“统一”的方式处理它们。由于您可能无法修改处理您的request 的服务的行为,这可能是唯一可行的选择。但是,如果您能够修改服务,您应该争取通过一次调用JSONDecoder.decode 即可解析的“统一”回复。您仍然需要解释选项(正如您在上面的示例中应该做的那样,因为使用它们仍然很痛苦,即使考虑到 Swift 出色的编译器支持迫使您“做正确的事情”),但它会使您的解析不易出错。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多