【问题标题】:Can Swift 4's JSONDecoder be used with Firebase Realtime Database?Swift 4 的 JSONDecoder 可以与 Firebase 实时数据库一起使用吗?
【发布时间】:2018-05-07 22:43:51
【问题描述】:

我正在尝试解码来自 Firebase DataSnapshot 的数据,以便可以使用 JSONDecoder 对其进行解码。

当我使用 URL 通过网络请求(获取 Data 对象)访问它时,我可以很好地解码这些数据。

但是,我想使用 Firebase API 直接获取数据,使用 this page 中描述的 observeSingleEvent。

但是,当我这样做时,我似乎无法将结果转换为数据对象,我需要使用 JSONDecoder。

是否可以使用 DataSnapshot 进行 JSON 解码的新样式?这怎么可能?我好像搞不懂。

【问题讨论】:

  • 从 Firebase 获取数据时不应使用 JSONDecoder。相反,您应该使用 Firebase API。

标签: json swift firebase-realtime-database json-deserialization


【解决方案1】:

没有。 Firebase 返回一个无法解码的 FIRDataSnapshot。但是,您可以使用这种结构,它非常简单易懂:

struct GroceryItem {
  
  let key: String
  let name: String
  let addedByUser: String
  let ref: FIRDatabaseReference?
  var completed: Bool
  
  init(name: String, addedByUser: String, completed: Bool, key: String = "") {
    self.key = key
    self.name = name
    self.addedByUser = addedByUser
    self.completed = completed
    self.ref = nil
  }
  
  init(snapshot: FIRDataSnapshot) {
    key = snapshot.key
    let snapshotValue = snapshot.value as! [String: AnyObject]
    name = snapshotValue["name"] as! String
    addedByUser = snapshotValue["addedByUser"] as! String
    completed = snapshotValue["completed"] as! Bool
    ref = snapshot.ref
  }
  
  func toAnyObject() -> Any {
    return [
      "name": name,
      "addedByUser": addedByUser,
      "completed": completed
    ]
  }
  
}

并使用 toAnyObject() 保存您的项目:

let groceryItemRef = ref.child("items")

groceryItemRef.setValue(groceryItem.toAnyObject())

来源:https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2

【讨论】:

  • 太棒了。这是我一直在寻找的方向。我没有意识到您可以以这种方式直接使用快照。对此进行修改很简单,因为我已经有了一个可解码的数据结构。而且,结果证明这个代码比原来的请求代码还要小!赢得胜利。
  • @ricardo 如果节点也有子节点怎么办,toAnyObject () - > Any 函数需要如何更改!?
  • @Learn2Code 您还需要在这些子项中使用“toAnyObject() -> Any”函数,例如:“object: yourObject.toAnyObject,”
  • @RicardoDaniel 你能看看下面的问题stackoverflow.com/questions/63911991/…。根据讨论,我似乎不能像您的回答所描述的那样轻易做到。
【解决方案2】:

我创建了一个名为 CodableFirebase 的库,它提供了专为 Firebase 设计的 EncodersDecoders

所以对于上面的例子:

import Firebase
import CodableFirebase

let item: GroceryItem = // here you will create an instance of GroceryItem
let data = try! FirebaseEncoder().encode(item)

Database.database().reference().child("pathToGraceryItem").setValue(data)

以下是读取相同数据的方式:

Database.database().reference().child("pathToGraceryItem").observeSingleEvent(of: .value, with: { (snapshot) in
    guard let value = snapshot.value else { return }
    do {
        let item = try FirebaseDecoder().decode(GroceryItem.self, from: value)
        print(item)
    } catch let error {
        print(error)
    }
})

【讨论】:

    【解决方案3】:

    我通过将快照转换回数据格式的 JSON,使用 JSONDecoder 转换了 Firebase 快照。您的结构需要符合 Decodable 或 Codable。我已经使用 SwiftyJSON 完成了此操作,但此示例使用 JSONSerialization 并且它仍然有效。

    JSONSnapshotPotatoes {
        "name": "Potatoes",
        "price": 5,
    }
    JSONSnapshotChicken {
        "name": "Chicken",
        "price": 10,
        "onSale": true
    }
    
    struct GroceryItem: Decodable {
        var name: String
        var price: Double
        var onSale: Bool? //Use optionals for keys that may or may not exist
    }
    
    
    Database.database().reference().child("grocery_item").observeSingleEvent(of: .value, with: { (snapshot) in
            guard let value = snapshot.value as? [String: Any] else { return }
            do {
                let jsonData = try JSONSerialization.data(withJSONObject: value, options: [])
                let groceryItem = try JSONDecoder().decode(GroceryItem.self, from: jsonData)
    
                print(groceryItem)
            } catch let error {
                print(error)
            }
        })
    

    请注意,如果您的 JSON 密钥与您的 Decodable 结构不同。您需要使用 CodingKeys。示例:

    JSONSnapshotSpinach {
        "title": "Spinach",
        "price": 10,
        "onSale": true
    }
    
    struct GroceryItem: Decodable {
        var name: String
        var price: Double
        var onSale: Bool?
    
        enum CodingKeys: String, CodingKey {
            case name = "title"
    
            case price
            case onSale
        }
    }
    

    您可以使用 Apple Docs here 找到更多信息。

    【讨论】:

    • 这可能是不使用任何第三方库的最佳解决方案。很棒的发现!
    【解决方案4】:

    您可以将 Firebase 返回的值转换为 Data,然后对其进行解码。

    将此扩展添加到您的项目中:

    extension Collection {
        //Designed for use with Dictionary and Array types
        var jsonData: Data? {
            return try? JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
        }
    }
    

    然后用它把观察到的快照的值转换成数据,然后就可以解码了:

    yourRef.observe(.value) { (snapshot) in
        guard snapshot.exists(),
           let value = snapshot.value as? [String],
           let data = value.jsonData else { 
           return
        }
        //cast to expected type
        do {
            let yourNewObject =  try JSONDecoder().decode([YourClass].self, from: data)
        } catch let decodeError {
            print("decodable error")
        }
    }
    

    【讨论】:

      【解决方案5】:

      或者您可以将此解决方案用于儿童

      extension DatabaseReference {
        func makeSimpleRequest<U: Decodable>(completion: @escaping (U) -> Void) {
          self.observeSingleEvent(of: .value, with: { snapshot in
              guard let object = snapshot.children.allObjects as? [DataSnapshot] else { return }
              let dict = object.compactMap { $0.value as? [String: Any] }
              do {
                  let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
                  let parsedObjects = try JSONDecoder().decode(U.self, from: jsonData)
                  completion(parsedObjects)
              } catch let error {
                  print(error)
              }
          })
        }
      }
      

      并使用

      self.refPriceStatistics.child(productId).makeSimpleRequest { (parsedArray: [YourArray]) in
          callback(parsedArray)
      }
      

      【讨论】:

        【解决方案6】:

        你可以使用这个库 CodableFirebase 或者下面的扩展可能会有所帮助。

        extension JSONDecoder {
        
        func decode<T>(_ type: T.Type, from value: Any) throws -> T where T : Decodable {
            do {
                let data = try JSONSerialization.data(withJSONObject: value, options: .prettyPrinted)
                let decoded = try decode(type, from: data)
                return decoded
        
            } catch {
                throw error
            }
        }
        

        【讨论】:

          【解决方案7】:

          如果你的数据类型是Codable,你可以使用下面的方案直接解码。您不需要任何插件。我使用了 Cloud Firestore 的解决方案。

          import Firebase
          import FirebaseFirestoreSwift
          
          
          let db = Firestore.firestore()
          let query = db.collection("CollectionName")
                      .whereField("id", isEqualTo: "123")
          
          guard let documents = snapshot?.documents, error == nil else {
              return
          }
          
          if let document = documents.first {
              do {
                  let decodedData = try document.data(as: ModelClass.self) 
                  // ModelClass a Codable Class
          
              }
              catch let error {
                  // 
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-03
            • 2019-10-23
            • 2018-07-27
            • 1970-01-01
            • 1970-01-01
            • 2018-03-22
            • 1970-01-01
            相关资源
            最近更新 更多