【问题标题】:Decoding json with optional fields使用可选字段解码 json
【发布时间】:2022-06-10 21:16:01
【问题描述】:

我正在尝试解析其中一些键未固定的 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。我试图建立我的类,如下所示,但它不起作用:

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 对象中得到 nil。

【问题讨论】:

  • 在您当前的 init(from:) 中,您应该检查 ok 是真还是假,然后解码结果或 errorCode 和错误。

标签: ios swift codable


【解决方案1】:

你应该把它分成几个步骤,以避免在你的模型中处理所有这些选项。

首先创建一个结构,它只具有那些保证存在的属性。 ok 在你的情况下:

struct OKResult: Codable{
    let ok: Bool
}

然后为您的错误状态和成功状态创建一个:

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)

备注:

  • 您的inits 不是必需的。
  • 永远不要使用try?,这将对您隐藏所有错误
  • 您需要将其包装在 do catch 块中,或者将您的函数设为 throwing 并在树的上层处理错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    • 2020-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多