Firebase Firestore返回FIRTimestamp而非Timestamp引发JSON序列化错误
解决Firestore FIRTimestamp无法JSON序列化的问题
问题原因
JSONSerialization仅支持Foundation原生的JSON兼容类型(如字符串、数字、数组、字典等),而FIRTimestamp不属于这类类型,直接序列化会抛出Invalid type in JSON write (FIRTimestamp)错误。你当前代码里的reservations是从Firestore获取的原始对象(包含FIRTimestamp实例),直接用JSONSerialization处理必然报错。
解决方案
方案1:直接用Firestore原生Codable支持(推荐)
Firestore已经内置Codable解码支持,无需通过JSONSerialization中转,直接将文档数据解码为你的ReservationInformation结构体:
// 单个文档解码 func fetchSingleReservation() async throws -> ReservationInformation { let docRef = Firestore.firestore().collection("reservations").document("your-doc-id") let docSnapshot = try await docRef.getDocument() return try docSnapshot.data(as: ReservationInformation.self) } // 批量文档解码 func fetchAllReservations() async throws -> [String: ReservationInformation] { let querySnapshot = try await Firestore.firestore().collection("reservations").getDocuments() var reservations = [String: ReservationInformation]() for doc in querySnapshot.documents { let reservation = try doc.data(as: ReservationInformation.self) reservations[doc.documentID] = reservation } return reservations }
方案2:手动转换FIRTimestamp为可序列化类型
如果确实需要生成JSON数据,需先将FIRTimestamp转换为JSON兼容类型(比如时间戳秒数、或包含秒/纳秒的字典),再进行序列化:
// 假设originalReservations是从Firestore拿到的原始字典 var serializableReservations = [String: [String: Any]]() for (key, reservationDict) in originalReservations { var dict = reservationDict // 将FIRTimestamp转成秒数(或秒+纳秒的字典) if let startTimestamp = dict["start"] as? FIRTimestamp { dict["start"] = startTimestamp.seconds } if let endeTimestamp = dict["ende"] as? FIRTimestamp { dict["ende"] = endeTimestamp.seconds } serializableReservations[key] = dict } // 现在可以正常序列化 let jsonData = try JSONSerialization.data(withJSONObject: serializableReservations, options: [])
方案3:自定义Codable处理逻辑
修改ReservationInformation的Codable实现,支持FIRTimestamp与Timestamp的转换:
import FirebaseFirestore struct ReservationInformation: Codable { let flinkeyId: String let kennzeichen: String let name: String let start: Timestamp let ende: Timestamp enum CodingKeys: String, CodingKey { case flinkeyId, kennzeichen, name, start, ende } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) flinkeyId = try container.decode(String.self, forKey: .flinkeyId) kennzeichen = try container.decode(String.self, forKey: .kennzeichen) name = try container.decode(String.self, forKey: .name) // 处理Firestore返回的FIRTimestamp if let firestoreTimestamp = try? container.decode(FIRTimestamp.self, forKey: .start) { start = Timestamp(seconds: firestoreTimestamp.seconds, nanoseconds: firestoreTimestamp.nanoseconds) } else { // 兼容JSON中的秒数格式 let seconds = try container.decode(Int64.self, forKey: .start) start = Timestamp(seconds: seconds, nanoseconds: 0) } // 同理处理ende字段 if let firestoreTimestamp = try? container.decode(FIRTimestamp.self, forKey: .ende) { ende = Timestamp(seconds: firestoreTimestamp.seconds, nanoseconds: firestoreTimestamp.nanoseconds) } else { let seconds = try container.decode(Int64.self, forKey: .ende) ende = Timestamp(seconds: seconds, nanoseconds: 0) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(flinkeyId, forKey: .flinkeyId) try container.encode(kennzeichen, forKey: .kennzeichen) try container.encode(name, forKey: .name) // 编码为秒数确保JSON兼容 try container.encode(start.seconds, forKey: .start) try container.encode(ende.seconds, forKey: .ende) } }
额外检查项
- Pod更新后,执行
pod install并clean项目(Cmd+Shift+K)后重新编译,确保依赖生效。 - 确认代码中导入的
Timestamp来自import FirebaseFirestore,而非其他同名类型。
内容的提问来源于stack exchange,提问作者Gian Dogwiler
相关产品推荐
相关产品推荐

