[英]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
对象而不必为其提供status
和data
属性?
正如其他答案所述,您基本上无法使用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
成为String
的RawRepresentable
,然后Swift可以使它成为Decodable
:
enum WTPStatus: String, Decodable {
case error
case success
}
对于故障响应类型,数据有效负载具有两个字段。 这听起来像Swift的struct
,并且由于字段是String
和Int
,因此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.