如何将递归数据映射到结构中?
Posted
技术标签:
【中文标题】如何将递归数据映射到结构中?【英文标题】:How to map recursive data into structure? 【发布时间】:2020-09-30 15:24:00 【问题描述】:这篇文章与previous post I made相关。我希望映射以下嵌套字典:
["A": [["A1": ["A11", "A12"]], ["A2": ["A21", "A22"]]],
"B": [["B1": ["B11", "B12"]], ["B2": ["B21", "B22"]]]
]
进入递归结构:
Item(title:"",children:
[Item(title:"A",children:
[Item(title:"A1", children:
[Item(title:"A11"),Item(title:"A12")]
)]),
Item(title:"B",children:
[Item(title:"B1"),Item(title:"B2")]
)]
)
与
struct Item: Identifiable
let id = UUID()
var title: String
var children: [Item] = []
为了实验,我从 ["A": [["A1": ["A11"]]] 开始,做了一个 json 字符串:
let json1: String = """
"title": "", "children":["title": "A",
"children": ["title": "A1",
"children": ["title": "A11"]
]
]
"""
let decoder = JSONDecoder()
let info = try decoder.decode(Item.self, from: json.data(using: .utf8)!)
print(info)
只有当我在最后一个节点中包含 "children": [] 时它才有效,如下所示:
let json2: String = """
"title": "", "children":["title": "A",
"children": ["title": "A1",
"children": ["title": "A11", "children": []]
]
]
"""
我需要做什么才能使json1字符串工作,这样即使没有children的输入,它也会采用[]的默认值?
【问题讨论】:
【参考方案1】:您必须提供自定义init(decoder:)
来处理这种情况。处理此问题需要您使用JSONDecoder
的容器的decodeIfPresent
API,并仅在值存在时尝试解码,并在条件失败时提供默认值。方法如下:
extension Item: Codable
enum CodingKeys: String, CodingKey
case title, children
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)
children = try container.decodeIfPresent([Item].self, forKey: .children) ?? []
【讨论】:
我刚刚尝试了您的解决方案,谢谢,它有效。我是 swift 新手,只想知道 init(decoder: ) 在哪一行被调用?是在“let info = try decoder.decode(Item.self, from: json.data(using: .utf8)!)”中吗?怎么叫 是的,当您尝试解码时。编译器调用该方法进行解码。【参考方案2】:一种方法是将children
设为可选并使用计算属性来访问它。在children
为nil
的情况下,计算属性可以返回一个空数组。
struct Item: Identifiable, Decodable
let id = UUID()
var title: String
private var privateChildren: [Item]?
var children: [Item]
return self.privateChildren ?? []
enum CodingKeys: String,CodingKey
case title
case privateChildren = "children"
我使用了CodingKeys
枚举,以便您可以在 JSON 和代码中保持相同的名称,但如果您在 JSON 或代码中更改了属性名称,则可以避免这种情况。
【讨论】:
我猜这里有些东西不起作用,它显示:Item(id: DEB3941C-F13C-425A-A34E-C1382B545E12, title: "", privateChildren: Optional([__lldb_expr_171.Item(id: D83563AA -5AF9-4B52-8480-9742178DC3BD,标题:“A”,privateChildren:可选([__lldb_expr_171.Item(id:B8163623-0A70-4C01-BA15-1148A42BA6FF,标题:“A1”,privateChildren:可选([__lldb_expr_171.Item (id: 05D09104-50BC-4050-AE43-5BF1754B2A29, title: "A11", privateChildren: nil)]))]))])) 我希望以“children”为key,最后一个privateChildren为nil但不是[]children
是一个计算属性,所以如果你只是print(item)
,它不会显示,但它会有你想要的值。试试print(item.children)
啊,明白了。对不起,这是一个严重的错误。最后一个孩子是 []: print(info.children[0].children[0].children[0].children) 显示 [].以上是关于如何将递归数据映射到结构中?的主要内容,如果未能解决你的问题,请参考以下文章
将数据从 Firestore 映射到 Swift 中的结构 - IOS