簡體   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