繁体   English   中英

Swift Parsing 使用 1 个 url api 解码 2 个不同的 json

[英]Swift Parsing decode 2 different json with 1 url api

嗨,我是 swift 的新手,我还在学习,所以我尝试制作登录控制器并解析 json 数据,如果它更正它解析带有 id 和内容的 json 数据,并且如果登录失败,那么 json 将显示一条消息。 我已经为所有需要的值数据创建了一个结构,但我收到了这个错误,表明它为零。

所以,如果登录成功,这是 json:

[ { "id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1" } ]

如果登录失败,这是 json:

[ { "message": "登录失败..", "status": "0" } ]

所以基本上它有一个相同的网址我猜? 但我不知道我有点卡在这里,我需要帮助

struct login : Codable {
    let id : Int
    let name : String
    let email : String
    let status : String
    let message : String

    init(dictionary : [String : Any]) {
        id = (dictionary ["id"] as? Int)!
        name = (dictionary ["name"] as? String)!
        email = (dictionary ["email"] as? String)!
        status = (dictionary ["status"] as? String)!
        message = (dictionary ["message"] as? String)!
    }

    enum CodingKeys : String, CodingKey {
        case id = "id"
        case name = "name"
        case email = "email"
        case status = "status"
        case message = "message"
    }
}

func Login() {

    let Email = EmailField.text!
    let Pass = PasswordField.text!


    print(api)

    guard let JsonUrl = URL(string: api) else {return}
    URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
        guard let data = data else {return}

        do{
            let parsing = try JSONDecoder().decode([login].self, from: data)
            print(parsing)
            self.Loginnn = parsing
            let stats = self.Loginnn.map { $0.status}

            if stats.contains("1"){
                print("Login Success")
                DispatchQueue.main.async {
                    self.appDelegate.loginSeque()
                }
            }else if stats.contains("0") {
                let action = UIAlertAction(title: "Got It", style:             .default, handler: nil)
                let alert = UIAlertController(title: "Wrong Email /   Password", message: "Please Try Again ", preferredStyle: .alert)
                alert.addAction(action)
                self.present(alert, animated: true, completion: nil)

                // so basicly i wanna run this alert action by search status if its contains "0"
            }
        }
    }catch{
        print(error)
    }
}.resume()
}

因此,当我尝试测试登录失败时,我没有在日志中的 json 中显示消息,而是显示此错误

"keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "没有与键 CodingKeys( stringValue: \\"id\\", intValue: nil) (\\"id\\").",underlyingError: nil))"

如果由于密码或电子邮件错误而导致登录失败,我只想弹出一些消息或警报.....所以也许有人可以帮助我如何以最佳方式做到这一点?

您可以声明成功和失败响应类型如下,

struct LoginSuccess: Decodable {
    var id: Int
    var name: String
    var email: String
    var status: String
}

struct LoginFailure: Decodable {
    var status: String
    var message: String
}

然后用作,

guard let JsonUrl = URL(string: api) else { return }
URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
    guard let data = data else { return }

        if let success = try? JSONDecoder().decode([LoginSuccess].self, from: data).first {
            GlobalVariable.UserId = String(success.id)
            DispatchQueue.main.async {                    
                 self.appDelegate.loginSeque()
            }
        } else if let failure = try? JSONDecoder().decode([LoginFailure].self, from: data).first {
            let action = UIAlertAction(title: "Got It", style:             .default, handler: nil)
            let alert = UIAlertController(title: "Wrong Email /   Password", message: failure.message, preferredStyle: .alert)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        }
}.resume()

成功响应仅包含键(“id”、“name”、“email”、“status”)

[ { "id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1" } ]

并且失败响应只包含键(“消息”,“状态”)

[ { "message": "登录失败..", "status": "0" } ]

如果您想对两个 JSON 响应使用相同的结构,您应该将属性设为可选

struct login : Codable {
    var id: Int?
    var name: String?
    var email: String?
    var status: String?
    var message: String?
}

此外,由于您的密钥与您的属性相同,因此如果您使用JSONDecoder().decode ,则不需要enum CodingKeysinit

在这种情况下,我会使用 JSONSerialization 将数据解码为 [[String: Any]] 并查看内容以确定它是哪种消息。

在我的代码中,我假设“状态”项告诉我们它是否成功登录,但例如可以查找“id”的存在或字典中元素的数量来确定响应的类型

do {
    let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
    if let response = result.first, let status = response["status"] as? String  {
        if status == "1" {
            if let id = response["id"] as? Int {
                let ids = String(id)
                //...
            }
        } else {
            if let message = response["message"] as? String {
                print(message)
            }
        }
    }
} catch {
    print(error)
}

以下是我在您问题中的代码中使用的解决方案。 请注意,我简化了登录结构,因为它仅在登录成功时使用

struct Login  {
    let id : Int
    let name : String
    let email : String
}

do {
    let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
    if let response = result.first, let status = response["status"] as? String  {
        if status == "1" {
            //handle success
            let login = Login(id: response["id"] as? Int ?? 0,
                              name: response["name"] as? String ?? "",
                              email: response["email"] as? String ?? "")
            self.Loginnn = login
            DispatchQueue.main.async {
                self.appDelegate.loginSeque()
            }
        } else {
            let action = UIAlertAction(title: "Got It", style: .default, handler: nil)
            let alert = UIAlertController(title: "Wrong Email /   Password", message: "Please Try Again ", preferredStyle: .alert)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        }
    }
} catch {
    print(error)
}

您已经为此得到了一个(或三个)答案,但我想向您展示如何在不使用JSONSerialization或推测性解码的情况下做到这一点。

所以我们有一些你想要解码的LoginSuccessLoginFailure类型:

struct LoginSuccess: Decodable {
    var id: Int
    var name: String
    var email: String
}

struct LoginFailure: Decodable {
    var message: String
}

我们希望根据与这些类型的字段位于同一容器中的status来区分它们。 所以我们创建一个enum

enum LoginResult: Decodable {
    case success(LoginSuccess)
    case failure(LoginFailure)

    enum Keys: CodingKey {
        case status
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        if try container.decode(String.self, forKey: .status) == "1" {
            self = .success(try LoginSuccess(from: decoder))
        } else {
            self = .failure(try LoginFailure(from: decoder))
        }
    }
}

注意, enuminit不会调用decoder.decode(LoginSuccess.self) 它传递给LoginSuccess初始值设定项的解码器。 LoginFailure相同。 这意味着这些初始值设定项将从与status字段相同的容器中提取值。

测试:

let successData = #"[ { "id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: successData))

// Output:
[__lldb_expr_1.LoginResult.success(__lldb_expr_1.LoginSuccess(id: 891, name: "User", email: "qdpim@immobisp.com"))]

let failureData = #"[ { "message": "Login Failed..", "status": "0" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: failureData))

// Output:
[__lldb_expr_1.LoginResult.failure(__lldb_expr_1.LoginFailure(message: "Login Failed.."))]

请注意,因为您的示例数据包含在[...] ,所以我解码了LoginResult数组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM