我对快速编程语言还很陌生,并且一直在努力使它在上个星期左右可用。我正在使用一个返回JSON数据的现有API,该数据的结构根据返回的场所数而略有变化。
实际结构有些复杂,但是此示例说明了该问题。一种结果是,我得到了一个返回的场地,例如:
{
"totalItems": 21,
"pageSize": 2,
"venues": {
"venue":
{
"name": "Venue Name 1"
"location": "Location A",
"showCount": "4"
}
}
}
另一种结果是,我得到了一系列返回场地:
{
"totalItems": 21,
"pageSize": 2,
"venues": {
"venue":
[{
"name": "Venue Name 1"
"location": "Location A",
"showCount": "4"
},
{
"name": "Venue Name 2"
"location": "Location B",
"showCount": "2"
}]
}
}
是的-该API的所有者应该返回一个数组,无论如何,但它们没有,并且不能更改。
我能够针对一系列场所对它进行正确解码(或者即使没有通过任何场所),但是当通过单个场所时,它会中止(由于结构的变化)。当我更改代码以容纳单个场所时,我的代码也起作用了,但是随后放弃了多个场所的返回。
我想做的就是将任何一种变体解码为一个包含数组的内部结构,而不管我收到哪种变体,从而使事后处理起来对于我来说要简单得多。像这样:
struct Response: Decodable {
let totalItems: Int
let pageSize: Int
let venues: VenueWrapper
struct VenueWrapper: Decodable {
let venue: [Venue] // This might contain 0, 1, or more than one venues
}
struct Venue: Decodable {
let name: String
let location: String
let showCount: Int
}
}
注意:在实际的JSON响应中,响应中实际上有几个这样的子结构(例如,单个结构与结构的数组),这就是为什么我觉得仅创建替代结构并不是一个好的解决方案。 / p>
我希望以前有人遇到过这个问题。预先感谢!
答案 0 :(得分:2)
您可以创建自己的解码器,
struct Response: Decodable {
let totalItems: Int
let pageSize: Int
let venues: VenueWrapper
struct VenueWrapper: Decodable {
var venue: [Venue]
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
venue = []
if let singleVenue = try? values.decode(Venue.self, forKey: CodingKeys.venue) {
//if a single venue decoded append it to array
venue.append(singleVenue)
} else if let multiVenue = try? values.decode([Venue].self, forKey: CodingKeys.venue) {
//if a multi venue decoded, set it as venue
venue = multiVenue
}
enum CodingKeys: String, CodingKey { case venue }
}
}
struct Venue: Decodable {
let name: String
let location: String
let showCount: String
}
}
答案 1 :(得分:1)
不需要VenueWrapper
。 ??
struct Response {
let totalItems: Int
let pageSize: Int
let venues: [Venue]
struct Venue {
let name: String
let location: String
let showCount: Int
}
}
您需要编写自己的初始化程序。不幸的是,我不认为有一种方法可以消除未经编码的属性的样板。
即使您永远不需要对其进行编码,制作Response
Codable
而不只是Decodable
也会使您可以访问自动生成的CodingKeys
。
extension Response: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
totalItems = try container.decode(Int.self, forKey: .totalItems)
pageSize = try container.decode(Int.self, forKey: .pageSize)
venues = try container.decode(key: .venues)
}
}
最后一行依赖于协议和扩展名,您可以将其用于类似“编码”的任何其他类型。 ?
protocol GoofilyEncoded: Codable {
/// Must have exactly one case.
associatedtype GoofyCodingKey: CodingKey
}
extension KeyedDecodingContainer {
func decode<Decodable: GoofilyEncoded>(key: Key) throws -> [Decodable] {
let nestedContainer = try self.nestedContainer(
keyedBy: Decodable.GoofyCodingKey.self,
forKey: key
)
let key = nestedContainer.allKeys.first!
do {
return try nestedContainer.decode([Decodable].self, forKey: key)
}
catch {
return try [nestedContainer.decode(Decodable.self, forKey: key)]
}
}
}
所有可能编码为数组的类型(或不是 ??️)都将需要一个单例枚举,如下所示:
extension Response.Venue: GoofilyEncoded {
enum GoofyCodingKey: CodingKey {
case venue
}
}