【问题标题】:Model for parsing a dynamic JSON解析动态 JSON 的模型
【发布时间】:2019-12-01 21:01:36
【问题描述】:

我遇到的端点可能会返回两种不同 JSON 之一,具体取决于用户是否需要回答安全问题:

// possible response (payload) #1
{
    "token": "123lknk123kj1n13132"
}

// possible response (payload) #2
{
    "securityQuestion": "What is your mother's maiden name?"
}

我的目标是创建一个模型,该模型将根据有效负载中存在的密钥(即"token""securityQuestion")对 JSON 进行不同的解码。 目前,我遇到了一个解析错误,我不知道为什么。

我的灵感来自对 SO 上一个问题的精心设计的回答。我当前的代码是它的修改版本,(理论上)符合我的需要。我希望我的代码的最终版本保留这种结构。我的代码如下:

/**
Enumerates the possible payloads received from Server

- success: Successful payload that contains the user's access token
- securityQuestion: Payload that contains the security question that the user has to answer to receive a token
*/
enum PayloadType: String, Decodable {
    case success
    case securityQuestion
}

protocol Payload: Decodable { static var payloadType: PayloadType { get } }

/// Model for successful response sent by the server.
struct SuccessfulResponse: Payload {
    static let payloadType = PayloadType.success
    let token: String
}

/// Model for response sent by the server which includes a security question
struct SecurityQuestionResponse: Payload {
    static let payloadType = PayloadType.securityQuestion
    let securityQuestion: String
}

/// Model for building a response sent by the server.
struct Response: Decodable {
    let data: Payload
    let payloadType: PayloadType

    init(from decoder: Decoder) throws {
        // NOTE*: This part is a little shaky, maybe this is where I am going wrong
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.payloadType = try values.decode(PayloadType.self, forKey: .payloadType)

        // payloadType will determine how the JSON is decoded
        switch self.payloadType
        {

        case .success:
            self.data = try values.decode(SuccessfulResonse.self, forKey: .data)

        case .securityQuestion:
            self.data = try values.decode(SecurityQuestionResponse.self, forKey: .data)
        }
    }

    private enum CodingKeys: String, CodingKey {
        case data
        case payloadType
    }
}

在发布这个问题之前,我查看了各种类似的帖子(123),但没有一个真正符合需求。

【问题讨论】:

  • 不要认为这对Codable 最好,您需要使用 SwiftyJSON/JSONSerialization 1 键不值得那么头痛
  • 你是对的,仅仅 1 把钥匙是不值得的。这种架构背后的基本原理是,服务器实际上可以发送多个响应,每个响应都有很多键。为了简洁起见,我决定不在这篇文章中列出它们,并避免过多的代码使访问者不堪重负。如果可能的话,我想避免使用外部库。此外,我对 JSONSerialization 并不精通,它可能需要我更改此模型的架构。你能帮我假设这个结构保持不变吗?
  • struct Root:Codable { let token,securityQuestion:String?}
  • 这是我尝试的第一件事。将securityQuestion/token 设为可选将不起作用。不知道为什么
  • @DataDaddy 如果您有有效的JSON 数据,请从该网址尝试app.quicktype.io

标签: ios json swift parsing


【解决方案1】:

我想建议一种不同的方法,不是更简单而是易于使用,至少对我来说更清楚。

struct Token: Codable { 
   let token: String?
}

struct SecurityQuestion:Codable {
    let securityQuestion: String?
}

在请求函数中添加以下内容

URLSession.shared.dataTask(with: url, completionHandler: {data,response,error in

    do {
        let responseObject = try JSONDecoder().decode(Token.self, from: data!)

        let token = responseObject.token
        print(token)
    } catch let parseError {
        print(parseError)
        do{
            let responseObject = try JSONDecoder().decode(SecurityQuestion.self, from: data!)
            let securityQuestion = responseObject.securityQuestion
            print(securityQuestion)
        }catch{
            print(error)
        }
    }
})

主要的想法是使用 catch 块尝试另一种解码类型,因为第一个失败了,如果你有许多不同的响应,你可以做更多的 catch 块来做一个新的解码类型,

【讨论】:

  • 这是一个可行的解决方案。我将在这里保持开放的态度并将其标记为已接受的答案,尽管我注意到的一件事是,如果有多个键(为简洁起见,我没有在帖子中包含),那么嵌套do-catch 语句很快就会变得丑陋
  • 这是真的,但是对于键,它将在init(from: Decode) func 中我有一个模型,其中解码的 func 比结构定义大,因为端点可以返回一个字符串 a doblue a int 或 a bool对于相同的值,所以如果一个痛苦的解码
猜你喜欢
  • 2019-12-27
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2013-12-20
  • 2021-10-18
  • 2021-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多