Swift 使用混合类型和可能的子结构解码 JSON
Swift Decode JSON with mixed types and possible sub structures
我必须解码来自 Swift 4 中的 API 的给定 JSON 结构。
问题是在树中的某个点,我在同一级别有不同类型的数据,其中一种类型可以有子元素。
我尝试了几种JSON解码器和可解码技术,但到目前为止我还没有找到解决方案。
简化 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?
您非常非常接近,并且在设计数据结构方面做得很好。您只需要尝试解码 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"))
}
}
此容器是单值容器,因为在解码的这一点上,您只解码一个事物、一组或一个项目。这取决于每个单一值来处理它们的子组件。
使用这个结构
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 }
}
希望对您有所帮助
我必须解码来自 Swift 4 中的 API 的给定 JSON 结构。 问题是在树中的某个点,我在同一级别有不同类型的数据,其中一种类型可以有子元素。
我尝试了几种JSON解码器和可解码技术,但到目前为止我还没有找到解决方案。
简化 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?
您非常非常接近,并且在设计数据结构方面做得很好。您只需要尝试解码 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"))
}
}
此容器是单值容器,因为在解码的这一点上,您只解码一个事物、一组或一个项目。这取决于每个单一值来处理它们的子组件。
使用这个结构
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 }
}
希望对您有所帮助