我必须解码来自Swift 4 API的给定JSON结构。 问题在于,在树中的某个点上,我在同一级别上具有不同种类的数据,而其中一个类型可以具有子元素。
我已经尝试了几种JSONDecoder和Decodable技术,但到目前为止,我还没有找到解决方案。
简化的JSON:
{
"menuName": "Menu 1",
"menuId": 1,
"menuGroups": [
{
"type": "group",
"name": "Group 1",
"menuEntry": [
{
"type": "group",
"name": "Sub Group 1.1",
"menuEntry": [
{
"type": "menuItem",
"productName": "Item 1",
"productPrice": "9.00"
},
{
"type": "menuItem",
"productName": "Item 2",
"productPrice": "12.00"
}
]
}, {
"type": "menuItem",
"productName": "Item 3",
"productPrice": "9.00"
}
]
}
]
}
以下是我要使用的可解码商品:
struct Menu: Decodable {
let menuName: String
let menuId: Int
let categories: [MenuCategory]
enum CodingKeys : String, CodingKey {
case menuName
case menuId
case categories = "menuGroups"
}
}
struct MenuCategory: Decodable {
let type: String
let name: String
let items: [CategoryItem]
enum CodingKeys : String, CodingKey {
case type
case name
case items = "menuEntry"
}
}
enum CategoryItem: Decodable {
case group(MenuCategory)
case menuItem(MenuItem)
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let item = try container.decode(MenuCategory.self)
self = .group(item)
return
} catch let err {
print("error decoding category: \(err)")
}
do {
let item = try container.decode(MenuItem.self)
self = .menuItem(item)
return
} catch let err {
print("error decoding item: \(err)")
}
try self.init(from: decoder)
}
}
struct MenuItem: Decodable {
let type: String
let productName: String
let productPrice: String
enum CodingKeys : String, CodingKey {
case type = "type"
case productName
case productPrice
}
}
我认为使用:
let container = try decoder.singleValueContainer()
是错误的,因为该容器不应该是一个单值容器,但是我不知道从这里选择/做什么...
有人对此有想法吗? 您将如何像示例中那样解码一些JSON?
答案 0 :(得分:1)
您非常非常亲密,并且在设计数据结构方面做得很好。您只需要尝试解码CategoryItem
中的每个可能选项即可。
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let item = try? container.decode(MenuCategory.self) {
self = .group(item)
} else if let item = try? container.decode(MenuItem.self) {
self = .menuItem(item)
} else {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
debugDescription: "Not a group or item"))
}
}
此容器是一个单值容器,因为在解码的这一点上,您只解码一件事,一个组或一个项目。取决于每个单独的值来处理其子组件。
答案 1 :(得分:1)
使用此结构
struct YourStruct: Codable {
let menuName: String
let menuID: Int
let menuGroups: [MenuGroup]
enum CodingKeys: String, CodingKey {
case menuName
case menuID = "menuId"
case menuGroups
}
}
struct MenuGroup: Codable {
let type, name: String
let menuEntry: [MenuGroupMenuEntry]
}
struct MenuGroupMenuEntry: Codable {
let type: String
let name: String?
let menuEntry: [MenuEntryMenuEntry]?
let productName, productPrice: String?
}
struct MenuEntryMenuEntry: Codable {
let type, productName, productPrice: String
}
在检查没有错误的数据任务中
if let data = data {
let decoder = JSONDecoder()
guard let decodedJson = try? decoder.decode(YourStruct.self, from: data) else { completion(nil) ; return }
}
希望获得帮助