我有一个 json,由于某种奇怪的原因,它返回两个不同的值,一个是数组,其余的是字典,这使得解析变得困难。
这是创建 JSON 元素的相关代码
func crtJson() -> [AnyHashable: Any] {
let myDays = [
"1": ["Chap": ["1":"0", "2":"0", "3":"0", "4":"0"], "Comp": "0"],
"2": ["Chap": ["5":"0", "6": "0", "7": "0", "8": "0"], "Comp": "0"],
"3": ["Chap": ["9":"0", "10": "0", "11": "0", "12": "0"], "Comp": "0"],
"4": ["Chap": ["13":"0", "14": "0", "15": "0", "16": "0", "17": "0"], "Comp": "0"],
"5": ["Chap": ["18":"0", "19": "0", "20": "0"], "Comp": "0"],
"6": ["Chap": ["21":"0", "22": "0", "23": "0"], "Comp": "0"],
"7": ["Chap": ["24":"0", "25": "0"], "Comp": "0"],
"8": ["Chap": ["26":"0", "27": "0", "28": "0"], "Comp": "0"],
"9": ["Chap": ["29":"0", "30": "0", "31": "0"], "Comp": "0"]
]
print(myDays)
let jsonObject: [String: Any] = [
"Days": myDays
]
let valid = JSONSerialization.isValidJSONObject(jsonObject) // true
print(jsonObject)
return jsonObject
}
您会注意到,除了 CHAP 元素的数量之外,我对其进行了完全相同的编码,这不会产生任何影响。
当我尝试使用以下代码阅读此内容时
func validateBook(mDay: Int) {
var intArray = [Any]()
let ref = Database.database()
let firUser = Auth.auth().currentUser
let myPath = "users/\(firUser!.uid)/Yrly/Days/\(mDay)/Chap"
let myRef1 = ref.reference().child(myPath)
print(myRef1.url)
myRef1.observe(.value, with: {
snapshot in
print(snapshot.value)
let myArray = snapshot.value as! NSArray
for n in 1...(myArray.count - 1) {
if (myArray[n] as! String) != "3" {
self.retVal = false
}
}
if self.retVal == true {
// Update day to completed
print("Day completed")
let ref = Database.database().reference()
let pathString = self.firuser! + "/Yrly/Days/\(String(mDay))/"
print(pathString)
let post = ["Comp": "3"]
ref.child("users").child(pathString).updateChildValues(post)
}
})
}
我得到第一个元素的以下值:(我的意思是第一个元素是第 1 天)
Optional(<__NSArrayM 0x600000db4300>(
<null>,
3,
0,
0,
0
)
)
对于所有其他元素:
Optional({
5 = 3;
6 = 0;
7 = 0;
8 = 0;
})
Could not cast value of type '__NSDictionaryM' (0x1f1d06c88) to 'NSArray' (0x1f1d02048).
那么为什么它将第一天作为数组读取,而将其他所有内容作为字典读取?
我尝试将以下行更改为字典:
let myArray = snapshot.value as! NSArray
以下: 让 myArray = snapshot.value 为! NS词典
它也会爆炸。 由于某种原因,它总是使第一个元素成为数组,而其他所有元素成为字典。
使用顺序数字索引作为 Firebase 实时数据库结构中的键是一种反模式,正是因为您遇到了这种行为。
Firebase 实时数据库将数组存储为带有数字键的键/值对。所以当你有一个数组时:
["first", "second", "third"]
Firebase 实际上将其存储为:
{
"0": "first",
"1": "second",
"2": "third"
}
然后,当客户端(REST API 或 SDK)读取此数据时,它会检测顺序键的模式并将其转换回数组。
当您有一个实际的数组时,这非常有用,但当您获取数据的子集(通过查询)时,或者当您有一个非数组对象,而您恰好使用(几乎)顺序数字作为键时,这会崩溃。根据确切的结果,SDK 可能会填充数组中缺失的条目(第一个结果中的
<null>
或完全不执行数组强制转换。
解决方案是遵循最佳实践,并且不使用数组索引作为 Firebase 实时数据库中的键。我经常使用的一个简单解决方法是在键前添加一个短字母数字值。
{
"key_0": "first",
"key_1": "second",
"key_2": "third"
}
另请参阅最佳实践:Firebase 中的数组(距今已有十多年历史了🤯)。