繁体   English   中英

如何在Swift4中验证此JSON?

[英]How to validate this JSON in Swift4?

有一个API,将其响应包装在具有status值和data值的关联数组中,其中data包含错误对象或期望值:

错误回应:

{
   "status":"error",
   "data":{  
      "errormessage":"Duplicate entry '101' for key 'PRIMARY'",
      "errorcode":1062
   }
}

成功回应:

{
   "status":"success",
   "data":{  
      "user": {
        "id": 1,
      }
   }
}

我想验证以下响应:

public class func validateResponse(_ data : Data) -> WebServicesError?
{
    struct WTPResponse : Decodable
    {
        let status : String
        let data : Data
    }

    do {
        let response = try JSONDecoder().decode(WTPResponse.self, from: data) // FAILS HERE
        if let wtpError = try? JSONDecoder().decode(WTPAPIError.self, from: response.data) {
            return WebServicesError.wtpError(WTPAPIError(code: wtpError.code, message: wtpError.message))
        }
    }
    catch let error {
        return WebServicesError.init(error: error)
    }

    return nil
}

当尝试对错误的响应对象进行解码时,它总是失败: Expected to decode Data but found a dictionary instead. 我当时以为我可以将data对象解码为Swift类型的Data ,但这实际上是[String: Any]字典。

1)如何验证从API收到的Data

2)有没有一种方法可以仅将JSON响应的“ data ”部分提取为Data类型,以便可以解码User对象而不必为其提供statusdata属性?

正如其他答案所述,您基本上无法使用JSONDecoder做到这一点,因为您无法为"data"键解码Data 您需要将其解码为Dictionary<String, Any>东西。 我可以想到一种方法,但是这样做很麻烦,即使那样,您最终还是使用Dictionary而不是Data ,所以您必须重新编码它才能使Data稍后传递给JSONDecoder

也许这意味着您必须下降到较低级别的JSONSerialization并“手动”浏览字典。 但是,如果您在解码时确切地知道要寻找哪种响应,那么我建议您使用Swift Decodable系统,而不要绕过它。

在最高级别,您有一个响应,可以是失败或成功,并且在每种情况下都携带不同的数据有效负载。 这听起来像带有关联值的Swift enum

enum WTPResponse {
    case failure(WTPFailure)
    case success(WTPSuccess)
}

我们希望它可以直接从JSON解码,但是我们必须手动编写Decodable一致性。 对于具有关联值的enum ,编译器无法自动执行。 在编写Decodable一致性之前,让我们定义我们需要的所有其他类型。

响应的类型由字符串"error"或字符串"success"标识,听起来像另一个Swift enum 我们可以使这个enum成为StringRawRepresentable ,然后Swift可以使它成为Decodable

enum WTPStatus: String, Decodable {
    case error
    case success
}

对于故障响应类型,数据有效负载具有两个字段。 这听起来像Swift的struct ,并且由于字段是StringInt ,因此Swift可以为我们提供Decodable功能:

struct WTPFailure: Decodable {
    var errormessage: String
    var errorcode: Int
}

对于成功响应类型,数据有效载荷是一个用户,该用户具有一个id: Int字段。 这听起来像是两个Swift struct ,Swift可以为我们提供Decodable功能:

struct WTPSuccess: Decodable {
    var user: WTPUser
}

struct WTPUser: Decodable {
    var id: Int
}

这涵盖了示例JSON中出现的所有内容。 现在,我们可以WTPResponse使WTPResponse符合Decodable ,如下所示:

extension WTPResponse: Decodable {
    enum CodingKeys: String, CodingKey {
        case status
        case data
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        switch try container.decode(WTPStatus.self, forKey: .status) {
        case .error: self = .failure(try container.decode(WTPFailure.self, forKey: .data))
        case .success: self = .success(try container.decode(WTPSuccess.self, forKey: .data))
        }
    }
}

这是一个测试:

let failureJsonString = """
    {
       "status":"error",
       "data":{
          "errormessage":"Duplicate entry '101' for key 'PRIMARY'",
          "errorcode":1062
       }
    }
"""

let successJsonString = """
    {
       "status":"success",
       "data":{
          "user": {
            "id": 1,
          }
       }
    }
"""

let decoder = JSONDecoder()
do {
    print(try decoder.decode(WTPResponse.self, from: failureJsonString.data(using: .utf8)!))
    print(try decoder.decode(WTPResponse.self, from: successJsonString.data(using: .utf8)!))
} catch {
    print(error)
}

这是输出:

failure(test.WTPFailure(errormessage: "Duplicate entry \'101\' for key \'PRIMARY\'", errorcode: 1062))
success(test.WTPSuccess(user: test.WTPUser(id: 1)))

这是Swift4 Codable无法使用的情况。 您必须手动解析JSON并注意各种情况。 https://github.com/SwiftyJSON/SwiftyJSON

正如shayegh所说,我不确定您将如何使用新的Codable功能来做到这一点。

您可以改用JSONSerialization类。 这样会将您的JSON数据转换为包含其他字典的Dictionary。 然后,您可以自己通过代码查询字典。

那将很容易。

我使用quicktype的多源模式为每种响应类型生成单独的Codable模型:

多模式

这是代码。 您可以先尝试解码Response ,如果失败,则可以尝试解码BadResponse

// let response = try? JSONDecoder().decode(Response.self, from: jsonData)
// let badResponse = try? JSONDecoder().decode(BadResponse.self, from: jsonData)

import Foundation

struct Response: Codable {
    let status: String
    let data: ResponseData
}

struct ResponseData: Codable {
    let user: User
}

struct BadResponse: Codable {
    let status: String
    let data: BadResponseData
}

struct BadResponseData: Codable {
    let errormessage: String
    let errorcode: Int
}

struct User: Codable {
    let id: Int
}

我认为这比尝试将其表示为单一类型稍微整洁。 我还建议不要选择性地解码JSON,而是解码所有JSON,然后从这些类型中选择所需的数据。

暂无
暂无

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

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