簡體   English   中英

使用可選字段解碼 json

[英]Decoding json with optional fields

我正在嘗試解析一個 json 字符串,其中一些鍵未固定。 有一些與錯誤相關的鍵,要么是錯誤,要么是結果數據。 以下是兩個例子:

{
    "ok": true,
    "result": {
        "code": "694kyH",
        "short_link": "shrtco.de\/694kyH",
        "full_short_link": "https:\/\/shrtco.de\/694kyH",
        "short_link2": "9qr.de\/694kyH",
        "full_short_link2": "https:\/\/9qr.de\/694kyH",
        "short_link3": "shiny.link\/694kyH",
        "full_short_link3": "https:\/\/shiny.link\/694kyH",
        "share_link": "shrtco.de\/share\/694kyH",
        "full_share_link": "https:\/\/shrtco.de\/share\/694kyH",
        "original_link": "http:\/\/google.com"
    }
}

{
    "ok": false,
    "error_code": 2,
    "error": "This is not a valid URL, for more infos see shrtco.de\/docs"
}

我將如何解析這個 JSON。我嘗試像下面這樣構建我的 class 但它不起作用:

struct ShortLinkData: Codable {
    let ok: Bool
    let result: Result?
    let errorCode: Int?
    let error: String?
    
    private enum CodingKeys : String, CodingKey { case ok, result, errorCode = "error_code", error }

       init(from decoder: Decoder) throws {
          let container = try decoder.container(keyedBy: CodingKeys.self)
          ok = try container.decode(Bool.self, forKey: .ok)
           result = try container.decode(Result.self, forKey: .result)
           errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
           error = try container.decodeIfPresent(String.self, forKey: .error)
      }
}

// MARK: - Result
struct Result: Codable {
    let code, shortLink: String
    let fullShortLink: String
    let shortLink2: String
    let fullShortLink2: String
    let shortLink3: String
    let fullShortLink3: String
    let shareLink: String
    let fullShareLink: String
    let originalLink: String

    enum CodingKeys: String, CodingKey {
        case code
        case shortLink = "short_link"
        case fullShortLink = "full_short_link"
        case shortLink2 = "short_link2"
        case fullShortLink2 = "full_short_link2"
        case shortLink3 = "short_link3"
        case fullShortLink3 = "full_short_link3"
        case shareLink = "share_link"
        case fullShareLink = "full_share_link"
        case originalLink = "original_link"
    }
    init(from decoder: Decoder) throws {
       let container = try decoder.container(keyedBy: CodingKeys.self)
        code = try container.decode(String.self, forKey: .code)
        shortLink = try container.decode(String.self, forKey: .shortLink)
        fullShortLink = try container.decode(String.self, forKey: .fullShortLink)
        shortLink2 = try container.decode(String.self, forKey: .shortLink2)
        fullShortLink2 = try container.decode(String.self, forKey: .fullShortLink2)
        shortLink3 = try container.decode(String.self, forKey: .shortLink3)
        fullShortLink3 = try container.decode(String.self, forKey: .fullShortLink3)
        shareLink = try container.decode(String.self, forKey: .shareLink)
        fullShareLink = try container.decode(String.self, forKey: .fullShareLink)
        originalLink = try container.decode(String.self, forKey: .originalLink)
   }
}

我的解析代碼:

let str = String(decoding: data, as: UTF8.self)
print(str)
let shortURL = try? JSONDecoder().decode(ShortLinkData.self, from: data)
return shortURL!

我總是在 shortURL object 中得到 nil。

你應該把它分成幾個步驟,以避免在你的 model 中處理所有這些選項。

首先創建一個結構,它只包含那些保證存在的屬性。 ok ,在你的情況下:

struct OKResult: Codable{
    let ok: Bool
}

然后為您的錯誤創建一個 state,為您的成功創建一個 state:

struct ErrorResult: Codable{
    let ok: Bool
    let errorCode: Int
    let error: String
    
    private enum CodingKeys: String, CodingKey{
        case ok, errorCode = "error_code", error
    }
}

struct ShortLinkData: Codable {
    let ok: Bool
    let result: Result
}

struct Result: Codable {
    let code, shortLink: String
    let fullShortLink: String
    let shortLink2: String
    let fullShortLink2: String
    let shortLink3: String
    let fullShortLink3: String
    let shareLink: String
    let fullShareLink: String
    let originalLink: String

    enum CodingKeys: String, CodingKey {
        case code
        case shortLink = "short_link"
        case fullShortLink = "full_short_link"
        case shortLink2 = "short_link2"
        case fullShortLink2 = "full_short_link2"
        case shortLink3 = "short_link3"
        case fullShortLink3 = "full_short_link3"
        case shareLink = "share_link"
        case fullShareLink = "full_share_link"
        case originalLink = "original_link"
    }
}

然后你可以解碼數據:

guard try JSONDecoder().decode(OKResult.self, from: data).ok else{
    let errorResponse = try JSONDecoder().decode(ErrorResult.self, from: data)
    //handle error scenario
    fatalError(errorResponse.error) // or throw custom error or return nil etc...
}

let shortlinkData = try JSONDecoder().decode(ShortLinkData.self, from: data)

評論:

  • 您的init不是必需的。
  • 從不使用try? 這將對您隱藏所有錯誤
  • 您需要將它包裝在一個do catch塊中,或者讓您的 function throwing錯誤並在樹上進一步處理錯誤。

實際上沒有可選字段 服務器發送兩個不同但不同的 JSON 字符串。

解碼兩個 JSON 字符串的合適方法是使用具有關聯值的枚舉。 它解碼ok鍵,然后解碼result字典或errorCodeerror

enum Response : Decodable {
    case success(ShortLinkData), failure(Int, String)
    
    private enum CodingKeys : String, CodingKey { case ok, result, errorCode, error }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let ok = try container.decode(Bool.self, forKey: .ok)
        if ok {
            let result = try container.decode(ShortLinkData.self, forKey: .result)
            self = .success(result)
        } else {
            let errorCode = try container.decode(Int.self, forKey: .errorCode)
            let error = try container.decode(String.self, forKey: .error)
            self = .failure(errorCode, error)
        }
    }
}

ShortLinkData中,如果您指定convertFromSnakeCase密鑰解碼策略,則init方法和CodingKeys是多余的

struct ShortLinkData: Decodable {
    let code, shortLink: String
    let fullShortLink: String
    let shortLink2, fullShortLink2: String
    let shortLink3, fullShortLink3: String
    let shareLink, fullShareLink: String
    let originalLink: String
}


do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Response.self, from: data)
    switch result {
        case .success(let linkData): print(linkData)
        case .failure(let code, let message): print("An error occurred with code \(code) and message \(message)")
    }
} catch {
    print(error)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM