我正在从Firebase下载一个大型文件结构。但是,当打印键和值时,其中一个子文件夹显示为<__NSArrayM>,我尝试将其转换为String:Any,但它正在崩溃。这是我的代码:
DATABASE.mainOrder.child("incomplete").observe(DataEventType.value, with: { (snapshot) in
let array = snapshot.value as! [String:[String:Any]]
for (_, value) in array {
for number in value {
if "\(number.key)" == "itemsInOrder" {
let items = number.value as! [String:Any]
//let items = Array(arrayLiteral: number.value)
print("itemsInOrder: \(items)")
} else {
print("\(number.key): \(number.value)")
}
}
}
})然而,它正在崩溃,并给了我这个错误:
不能将“__NSArrayM”(0x2037d2608)类型的值转换为“NSDictionary”(0x2037d21d0)。
这是我想要得到的代码:
itemsInOrder: [<__NSArrayM 0x281c060a0>({
amount = "2";
length = "5";
height = "7";
width = "10";
})]下面是Firebase json导出
{
"TH000" : {
"docUUIDStatus" : "Sent",
"expectedShip" : "May 12, 2021",
"itemsInOrder" : [ {
"amountOrdered" : "2000 sq ft",
"bevel" : "None",
"floorLength" : "2-8",
"floorWidth" : "4",
"specialOrder" : "None",
"specialOrderLength" : "",
"status" : "completed"
} ],
"orderDate" : "May 11, 2021",
"orderNumber" : "TH000",
"orderNumberLowercased" : "th000",
"orderTime" : "2:30:30 PM",
"orderType" : "Order",
"purchaseOrderNumber" : "TH10051",
"purchaseOrderNumberLowercased" : "th10051",
"status" : "completed"
}
}发布于 2021-05-14 14:00:58
弗兰克给出的答案是正确的。我想提出另一种办法。
我们已经发现,DataSnapshots非常灵活,而且易于使用--尽可能长时间地将数据作为一个DataSnapshot来保存,尤其是在结构有点深的情况下。当转换到数组和字典时,你会得到这样的结果
[String: [String: [String: Any]]]这是非常麻烦的工作和疑难解答。
这是按照问题中指定的数据读取的代码。注意,我们从不将任何内容转换为字典,并使用数组来保证从检索到的DataSnapshots中保持排序。
func readData() {
let ref = self.ref.child("main_order").child("incomplete") //self.ref points to my firebase
ref.observe(.value, with: { snapshot in //first node will be TH000
let childSnapArray = snapshot.children.allObjects as! [DataSnapshot]
for childSnap in childSnapArray {
print("parent node: \(childSnap.key)")
let status = childSnap.childSnapshot(forPath: "docUUIDStatus").value as? String ?? "No Status"
print(" status: \(status)")
let itemsInOrder = childSnap.childSnapshot(forPath: "itemsInOrder") as DataSnapshot
let itemSnapArray = itemsInOrder.children.allObjects as! [DataSnapshot]
print(" \(itemsInOrder.key)")
for itemChildSnap in itemSnapArray {
let amount = itemChildSnap.childSnapshot(forPath: "amountOrdered").value as? String ?? "No amount"
let length = itemChildSnap.childSnapshot(forPath: "floorLength").value as? String ?? "No length"
let width = itemChildSnap.childSnapshot(forPath: "floorWidth").value as? String ?? "No width"
let status = itemChildSnap.childSnapshot(forPath: "status").value as? String ?? "No status"
print(" ", amount, length, width, status)
}
}
})
}在此过程中,我添加了几个print语句,以显示代码执行顺序。控制台的输出如下
parent node: TH000
status: Sent
itemsInOrder
2000 sq ft 2-8 4 completed另一件事是存储在itemsInOrder中的数组。还不清楚为什么这是一个数组,通常在实时数据库中应该尽可能避免数组。通常有更好的方法来构造数据。数组不能直接更改--如果要插入或删除元素,则必须读取、修改和写入整个数组。
因此,如果有多个数组元素,那么上面的代码可能需要稍微修改以处理itemsInOrder节点,例如。
发布于 2021-05-13 14:27:16
您在itemsInOrder中拥有的是一个字典数组,因此要从其中获取数据,要么必须在嵌套循环中遍历number,要么使用以下方法提取:
let items = number.value as! [[String:Any]]因为我觉得第一眼就很难看到双[[,所以我实际上更喜欢这种等价的语法:
let items = number.value as! [Dictionary<String:Any>]与实际问题无关:在Firebase中,数据结构有点反模式,因为读取任何订单现在也意味着以该顺序加载所有项目。当您想要显示一个订单名称列表时,这会变得很浪费。
数据结构越惯用,它就有两个顶级节点:
在第一个节点中,您将每个订单的属性(如orderType和status )存储在order ID下;在第二个节点中,存储订单时间列表,也存储在order ID下。
使用该结构,当前的查询将需要两个步骤:
这第二步并不像您想象的那么慢,就像Firebase pipelines the requests over a single existing connection一样。
https://stackoverflow.com/questions/67509175
复制相似问题