Swift解析使用1个URL API解码2个不同的json

时间:2019-06-19 04:54:07

标签: json swift parsing decode

您好,我是Swift的新手,我还是在学习,所以我尝试制作登录控制器并解析json数据(如果它纠正了它解析的ID和东西的json数据),并且如果登录失败,那么json将显示一种信息。我已经为所需的所有值数据制作了一个结构,但是出现了表示为零的错误。

因此,如果登录成功,则为json:

  

[       {           “ id”:891,           “ name”:“ User”,           “电子邮件”:“ qdpim@immobisp.com”,           “状态”:“ 1”       }]

如果登录失败,这是json:

  

[       {           “ message”:“登录失败..”,           “状态”:“ 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:“索引   0“,intValue:0)],debugDescription:“没有与键关联的值   CodingKeys(stringValue:\“ id \”,intValue:nil)(\“ id \”)。“,   底层错误:nil))“

我只是想弹出一些消息或警告,如果由于密码或电子邮件错误或登录失败而导致登录失败.....那么有人可以帮我最好的方法吗?

4 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

成功响应仅包含键(“ 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?
}

此外,由于密钥与属性相同,因此如果您使用enum CodingKeys

,则不需要initJSONDecoder().decode

答案 2 :(得分:0)

在这种情况下,我将使用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)
}

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

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

答案 3 :(得分:0)

您已经对此有了一个答案(或三个答案),但是我想向您展示如何在不使用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))
        }
    }
}

请注意,enum的{​​{1}}不会调用init。它传递给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.."))] 中,因此我解码了[...]的数组。