json - 使用混合类型和可能的子结构快速解码 JSON
问题描述
我必须解码来自 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?
解决方案
您非常非常接近,并且在设计数据结构方面做得很好。您只需要尝试解码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"))
}
}
这个容器是一个单值容器,因为在解码的这一点上,你只解码一个东西,一个组或一个项目。由这些单个值中的每一个来处理它们的子组件。
推荐阅读
- mongodb - Kafka MongoDB 从表中只接收一条记录
- python - 如何在sqlalchemy async中获取具有特定属性的所有id列表
- java - LocalTime.MIDNIGHT 与 LocalTime.MIN - 有什么区别吗?
- c# - xmlunit 比较格式化程序没有被替换
- flutter - 如何从 webview flutter 中删除 Header 并在底部导航图标中添加下划线
- visual-studio-code - 运行多个 NPM 命令 vscode 任务
- java - 如何在postgres中比较时间戳数据类型和字符串?
- c - 如何在 riscv 中将 .elf 文件反汇编为 .asm 文件
- reactjs - React 最佳实践将复选框绑定到模型
- flask-jwt-extended - 在 flask-jwt-extended 如何设置自定义令牌到期时间?