【问题标题】:Swift Parsing decode 2 different json with 1 url apiSwift Parsing 使用 1 个 url api 解码 2 个不同的 json
【发布时间】:2019-11-01 18:09:55
【问题描述】:

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

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

[ { “身份证”:891, “名称”:“用户”, “电子邮件”:“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))"

我只是想在登录失败时弹出一些消息或警报,因为密码或电子邮件错误.....所以也许有人可以帮助我如何以最好的方式做到这一点?

【问题讨论】:

  • 是的,我的 json id 是:Int not string

标签: json swift parsing decode


【解决方案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()

【讨论】:

  • 它还有这个 ("keyNotFound(CodingKeys(stringValue: "id", intValue: nil)" ) 先生
  • 什么是崩溃?哪条线路崩溃了?
  • 它有效,先生,非常感谢你,它只是那个警报操作导致的崩溃......我仍然想通了,但有什么帮助吗?
  • @afipermana 您可以在问题中添加该错误,或​​者如果此问题中的问题已解决,您可以提出另一个问题。
【解决方案2】:

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

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

并且失败响应只包含键(“message”、“status”)

[ { "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

【讨论】:

  • 谢谢,但我已经尝试过了,我不工作,因为我想用这行代码使那个 id 成为全局变量: let ids = self.Loginnn.map {$0.id} if let id = ids.first{ GlobalVariable.UserId = String(id) } 并且我收到一个错误(“无法使用类型为 '(Int?)' 的参数列表调用类型为 'String' 的初始化程序”)并且当我尝试使用“!”在我的 : (" GlobalVariable.UserId = String(id!) ") 中,每次我尝试登录失败时都会崩溃
  • @afipermana 这似乎与您的问题中所述的问题不同。您的问题是关于该错误的,该错误的原因是 JSONDecoder 期望 JSON 响应中存在所有值,并且当它在错误响应中找不到 id 时不知道该怎么做。如果您有其他问题,请作为新问题提出
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    您已经得到了一个(或三个)答案,但我想向您展示如何在不使用 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 的数组。

    【讨论】:

    • 哇,谢谢你......因为我是 Ios 的新手,这让我想出了新的解码方式......非常感谢!!!!
    猜你喜欢
    • 2015-09-02
    • 2017-10-25
    • 1970-01-01
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    • 2018-02-22
    相关资源
    最近更新 更多