【问题标题】:How do I fix this error of "Could not cast value of type '__NSCFNumber' (0x116e337d8) to 'NSDictionary' (0x116e34818)."如何修复“无法将'__NSCFNumber'(0x116e337d8)类型的值转换为'NSDictionary'(0x116e34818)”的错误。
【发布时间】:2019-04-18 00:19:03
【问题描述】:

我正在尝试访问嵌套的 firebase 数据库,特别是自动 ID 下的 Rating 的子级,但如何正确访问它? JSON 结构如下所示:

users
 Q6jYe9gzAePy8x6SJulTYLMQTpN2
   Fname: Mark Jed
   Lname: Venge
   Rating:
     18e85e0a-9b91-4cb1-a743-792212f1925f
       Rating: 3
       User: k8rn16AAXbTLSyvinD92CugMbL53

这是sn-p:

for child in snapshot.childSnapshot(forPath: "Rating").children{

  let child = child as! DataSnapshot

  for ratings in child.children{

    let valueSnap = ratings as! DataSnapshot
    let dict = valueSnap.value! as! [String:Any]

  }

}

问题是“dict”有一个错误

无法将“__NSCFNumber”(0x116e337d8) 类型的值转换为“NSDictionary”(0x116e34818)。

【问题讨论】:

  • 试试let dict = valueSnap.value as? Float
  • valueSnap.value 的实际类型是什么?
  • 请(学会)阅读错误信息。这很清楚。左侧类型为实际类型(NSNumber - Swift IntDouble),右侧类型为错误类型(NSDictionary -斯威夫特[String:Any])

标签: ios swift firebase firebase-realtime-database


【解决方案1】:

snapshot.children 上有两个嵌套循环,这似乎太多了。

看起来您从用户的快照开始,然后循环遍历其Rating 属性的子级。

for child in snapshot.childSnapshot(forPath: "Rating").children{

所以在这个循环中,child 快​​照指的是这个:

Rating: 3
User: k8rn16AAXbTLSyvinD92CugMbL53

然后您再次遍历这些孩子并尝试将它们中的每一个读取为[String:Any]。这是行不通的,因为这里的每个属性都是一个值。由于您不能将3 转换为[String:Any],因此应用程序崩溃了。

有两种选择:

  1. 去掉内循环:

    for child in snapshot.childSnapshot(forPath: "Rating").children{
      let child = child as! DataSnapshot
      let dict = child.value! as! [String:Any]
    }
    
  2. 不要转换为[String:Any],而只是获取简单的值:

    for child in snapshot.childSnapshot(forPath: "Rating").children{
    
      let child = child as! DataSnapshot
    
      for ratings in child.children{
        let valueSnap = ratings as! DataSnapshot
        let value = valueSnap.value! as! Any
      }    
    }
    

编辑

为了避免'Any',下面的代码会直接获取每个子节点的评分和用户值。

let ratingsRef = self.ref.child("users").child("the user id")
ratingsRef.observeSingleEvent(of: .value, with: { snapshot in
    let allRatingsSnap = snapshot.childSnapshot(forPath: "Rating")
    for ratingSnap in allRatingsSnap.children.allObjects as! [DataSnapshot] {
        let rating = ratingSnap.childSnapshot(forPath: "Rating").value ?? "no rating"
        let uid = ratingSnap.childSnapshot(forPath: "User").value ?? "no user"
        print("user uid: \(uid)  rating: \(rating)")
    }
})

和输出

user uid: uid_1  rating: 3
user uid: uid_2  rating: 2

【讨论】:

  • 不要投射到 Any
  • @vadian:问题中显示的两个属性具有不同的类型,因此循环不可能转换为任何特定类型。如果您觉得有更好的方法来处理用例,请发布显示您建议的解决方案的答案。
  • 在这种情况下,我对@vadian 的“任何”很友好......任何都是如此......通用,哈哈。我认为底线是 OP 想要特定子节点的值。我添加了一个稍微扩展的解决方案,以直接访问这些节点。
  • 感谢杰伊的编辑!一旦达到属性级别,如果使用命名节点而不是循环子节点,确实会好得多。顺便说一句:我认为您的代码中不需要嵌套循环。
  • @FrankvanPuffelen 嵌套循环......这个? for ratingSnap in allRatingsSnap.chi...?如果是这样...请检查我..我们正在读取用户节点,然后是子节点 Rating,我假设将有许多子节点 - 问题中只显示一个但我认为它的要点是会有多个孩子;该结构将是 Rating/rating_id_0/rating 和 Rating/rating_id_0/user。接下来是 Rating/rating_id_1/rating 和 Rating/rating_id_1/user 等。所以我们需要遍历每个 rating_id 节点。当然,我也可能完全错了! ;-)
猜你喜欢
  • 1970-01-01
  • 2017-09-26
  • 2015-08-29
  • 2017-04-09
  • 2018-12-13
  • 1970-01-01
  • 2020-08-04
  • 2018-02-08
相关资源
最近更新 更多