【问题标题】:Getting a particular value from different JSON responses in swift?快速从不同的 JSON 响应中获取特定值?
【发布时间】:2020-11-28 19:55:22
【问题描述】:

我有一个模型叫 College

class College : Decodable {
    let name : String
    let id : String
    let iconUrl : String
}

还有一些与大学相关的 API,每个 API 的响应都略有不同。 2个例子是

  1. 获取 api/v1/colleges 此 API 的响应 JSON 是

    { “成功”:字符串, “学院”:[学院] }

  2. 获取 api/v1/college/{collegeID} 此 API 的响应 JSON 是

    { “成功”:字符串, “学院”:学院 }

现在,从我只需要获取大学信息的两个响应中,“成功”键对我没有用。我的问题是,如何在不为每个 API 创建单独的响应模型的情况下获取大学信息?目前我已经为每个 API 响应实现了单独的类

class GetCollegesResponse : Decodable {
    let success : String
    let colleges : [College]
}
 
class GetCollegeResponse : Decodable {
    let success : String
    let college : College
}

我在各自的 API 调用中使用它们,就像这样

Alamofire.request(api/v1/colleges ....).responseJSON { response in
    let resp = JSONDecoder().decode(GetCollegesResponse.self, response.data)
    //get colleges from resp.colleges
}
 
Alamofire.request(api/v1/college/\(id) ....).responseJSON { response in
    let resp = JSONDecoder().decode(GetCollegeResponse.self, response.data)
    // get college form resp.college
}

有没有更简单的方法来完成这项工作?

【问题讨论】:

  • 您希望最终结果是什么?您是否希望它始终为[College],即使是单个结果?这里有多种方法,具体取决于您想要实现的目标。例如,您还可以将其设为通用响应类型,如下所示:APIResponse<T: Decodable> { let success: String, let payload: T }
  • 不,我希望第一个具有大学数组,而另一个只有一个大学对象。最后,我的问题归结为,如何在不为整个响应创建模型的情况下从 json 响应中获取特定值?如果有办法做到这一点,那么无论是 College 还是 [College] 都没关系,因为我们可以使用 [College].self 获得大学数组。
  • 我先写了一个答案,后来才发现API响应中的key也不一样。实际上,您从服务器获得了相当不同的响应,其中密钥可能会根据响应的内容而改变-即,不仅值类型不同。那么,什么决定了密钥呢?
  • 我想知道是否有一种方法可以通过非常简单的步骤获取内部对象,例如最多 2 到 3 行代码。我唯一的问题是,对于每个 API 响应,我都必须创建一个新模型,这会不必要地增加文件数量和复杂性。但是,如果不摆弄您在回答中提到的泛型,似乎没有办法做到这一点。我想要的是 1. 以某种方式只提取我想要的密钥,就像它完成的那样是 swiftyJSON let collegeEncoded = response["college"] 2. 使用解码器解码 let college = JSONDecoder().decode(College.self,collegeEncoded)

标签: ios json swift alamofire decodable


【解决方案1】:

可能正确的方法是将响应建模为泛型类型,如下所示:

struct APIResponse<T: Decodable> {
   let success: String
   let payload: T
}

您可以从中提取有效负载。

问题在于有效载荷的键发生了变化:college 用于单个结果,colleges 用于多个大学结果。

如果您真的不在乎并且只想要有效负载,我们可以有效地忽略它并将任何密钥(“成功”除外)解码为预期类型T

struct APIResponse<T: Decodable> {
   let success: String
   let payload: T

   // represents any string key
   struct ResponseKey: CodingKey {
      var stringValue: String
      var intValue: Int? = nil

      init(stringValue: String) { self.stringValue = stringValue }
      init?(intValue: Int) { return nil }
   }

   init(from decoder: Decoder) throws {
      let container = try decoder.container(keyedBy: ResponseKey.self)
      
      let sKey = container.allKeys.first(where: { $0.stringValue == "success" })
      let pKey = container.allKeys.first(where: { $0.stringValue != "success" })

      guard let success = sKey, let payload = pKey else {
         throw DecodingError.keyNotFound(
            ResponseKey(stringValue: "success|any"),
            DecodingError.Context(
               codingPath: container.codingPath, 
               debugDescription: "Expected success and any other key"))
      }

      self.success = try container.decode(String.self, forKey: success)
      self.payload = try container.decode(T.self, forKey: payload)
   }
}

然后您可以根据预期的有效载荷进行解码:

let resp = try JSONDecoder().decode(APIResponse<[College]>.self, response.data)
let colleges = resp.payload

【讨论】:

    【解决方案2】:

    恐怕如果不为整个响应创建模型,就无法从 json 响应中获取特定项目的值。 (至少使用可编码)

    首先,我们会以编码格式 (UTF8) 接收从服务器发送的有效载荷。所以我们必须在使用它之前先对其进行解码,这就是 codable 帮助我们解码数据的地方。如果您希望使用字符串查看原始转换,请尝试此方法。

    let dataConvertedToString = String(data: dataReceivedFromServer, encoding: .utf8)
    

    如果您仍然喜欢仅从 JSON 响应中获取值,我建议您使用 SwiftyJSON。它是一个 cocoapod 框架。你可以像这样使用 SwiftyJSON。

    let json = try! JSON(data: dataFromServer)
    json["success"].boolValue
    json["college"]["name"].stringValue
    

    【讨论】:

    • 项目中一直使用 swiftyJSON 进行解析,但是我想减少对第三方库的依赖,所以我正在考虑切换到 Codable 来获取新的 API。我开始的那一刻就遇到了这个问题。我想除了为每个响应制作模型然后提取所需的数据之外,别无他法。
    猜你喜欢
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多