繁体   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