如果某些东西符合 Codable ,它会永远无法被编码或解码吗?

Posted

技术标签:

【中文标题】如果某些东西符合 Codable ,它会永远无法被编码或解码吗?【英文标题】:If something conforms to Codable can it EVER fail to be Encoded or Decoded? 【发布时间】:2021-07-16 15:15:53 【问题描述】:

我想知道符合Codable 的对象在编码或解码过程中是否会导致错误。我觉得好像符合协议确保永远不会抛出错误。

例如,如果我有一个名为 Bookstruct

struct Book: Codable, Identifiable 
    @DocumentID var id: String?
    var name: String

这个struct永远会不会被编码或解码失败? 例如,如果我这样做:

try! docRef.setData(from: book)  error in _ 

如果Book 符合Codable,我认为这永远不会同步抛出错误。

同样,

try! docRef.data(as: Book.self)

应该从不抛出,因为Book再次符合Codable

如果我错了,Book 在编码或解码时可能会失败,请说明在什么条件/情况下可能会发生。

提前致谢!

【问题讨论】:

如果您拥有并控制自定义类型和数据源,那么一旦经过正确测试,它就永远不会失败。 @JoakimDanielson 正确测试是什么意思?我确实控制了“将数据放入 Firestore”以及“从 Firestore 中检索数据”这两个方面。当我编码和上传数据然后下载和解码数据时,它工作得很好。这是否意味着它已经过正确测试? 通过适当的测试,我相信 Joakin 意味着它理论上应该可以工作并且永远不会失败。只要您有足够的控制来确保数据准确且格式正确。单元测试将有助于确保这一点 @Scriptable 非常感谢您的回答。我已经为这件事烦恼了两天了。超级超级感谢:D 是的,经过适当测试,我的意思是在开发阶段之后进行测试,以便编码/解码按预期工作。 【参考方案1】:

正如您在 Firestore 的背景下提出的这个问题,除了 Ralf 在他的回答中提到的内容之外,我想添加更多细节。

在解码 Firestore 文档时,有许多情况可能会导致映射错误,这就是您应该准备好处理它们的原因。

例如:

您的结构可能需要一个字段(即它是非可选的),但 Firestore 文档中缺少该字段。当您有其他应用(android、Web 甚至您的 ios 应用的早期版本)写入同一文档但使用早期版本的架构时,可能会发生这种情况 文档中的一个(或多个)属性的数据类型可能与您的结构不同。

以下代码 sn-p 包含针对这些情况和其他情况的大量错误处理(请注意,您需要根据个人情况调整处理这些错误的方式 - 您可能希望也可能不希望显示用户可见的错误消息,例如)。代码摘自我的文章Mapping Firestore Data in Swift - The Comprehensive Guide。

class MappingSimpleTypesViewModel: ObservableObject 
  @Published var book: Book = .empty
  @Published var errorMessage: String?
  
  private var db = Firestore.firestore()
  
  func fetchAndMap() 
    fetchBook(documentId: "hitchhiker")
  
  
  func fetchAndMapNonExisting() 
    fetchBook(documentId: "does-not-exist")
  
  
  func fetchAndTryMappingInvalidData() 
    fetchBook(documentId: "invalid-data")
  
  
  private func fetchBook(documentId: String) 
    let docRef = db.collection("books").document(documentId)
    docRef.getDocument  document, error in
      if let error = error as NSError? 
        self.errorMessage = "Error getting document: \(error.localizedDescription)"
      
      else 
        let result = Result  try document?.data(as: Book.self) 
        switch result 
        case .success(let book):
          if let book = book 
            // A Book value was successfully initialized from the DocumentSnapshot.
            self.book = book
            self.errorMessage = nil
          
          else 
            // A nil value was successfully initialized from the DocumentSnapshot,
            // or the DocumentSnapshot was nil.
            self.errorMessage = "Document doesn't exist."
          
        case .failure(let error):
          // A Book value could not be initialized from the DocumentSnapshot.
          switch error 
          case DecodingError.typeMismatch(let type, let context):
            self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
          case DecodingError.valueNotFound(let type, let context):
            self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
          case DecodingError.keyNotFound(let key, let context):
            self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
          case DecodingError.dataCorrupted(let key):
            self.errorMessage = "\(error.localizedDescription): \(key)"
          default:
            self.errorMessage = "Error decoding document: \(error.localizedDescription)"
          
        
      
    
  

如果您对 Firestore 如何实现 Codable 协议感到好奇,请访问source code。例如,搜索 DecodableError 将显示哪些错误情况可能发生的确切程度以及原因。

【讨论】:

谢谢.. 我的映射失败了,我不明白为什么。属性中的拼写错误。谢谢!【参考方案2】:

编码:

对于 JSONEncoder,此处记录了编码失败的情况:https://developer.apple.com/documentation/foundation/jsonencoder/2895034-encode。 Afaik 假设所有属性和包含的数据结构都由简单的类型组成,如 Int、String、Array、Dictionary,它永远不会失败,因为这些值总是可以表示为 JSON。但是你可能有一个像 Float 这样的类型,当值不能表示为 JSON 时,它会抛出一个错误。

解码:

任何无效的 JSON 输入(例如缺少字段或语法错误)都会引发错误。我建议始终处理这些问题并确保它们不会被忽视/用户至少会获得“抱歉,出了点问题,我们正在调查”的体验。

【讨论】:

您是否建议也对 book.id ?? "" 之类的东西使用 nil 合并?或者对@DocumentID 属性使用强制解包很好?即book.id!

以上是关于如果某些东西符合 Codable ,它会永远无法被编码或解码吗?的主要内容,如果未能解决你的问题,请参考以下文章

Codable : 不符合协议'Decodable'

符合 Codable 协议的类因 encodeWithCoder 失败:无法识别的选择器发送到实例

如何使 UIImage 符合 Codable?

如何在 Codable 中使用 List 类型? (RealmSwift)

Codable 类不符合协议 Decodable

Swift - 如何使这个结构符合 Codable?