Swift 将 Decodable 结构转换为 Dictionary
Swift converting Decodable struct to Dictionary
我创建了一个可分解的结构,用于解析存储在 firebase 中的数据。
struct DiscussionMessage: Decodable {
var message: String
var userCountryCode: String
var userCountryEmoji: String
var messageTimestamp: Double
var userName: String
var userEmailAddress: String
var fcmToken: String?
var question: String?
var recordingUrl: String?
}
我也想使用这个结构在 firebase 中存储数据。但我得到错误:
*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(setValue:) Cannot store object of type __SwiftValue at . Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.'
terminating with uncaught exception of type NSException
当我这样存储数据时:
let message = DiscussionMessage(message: messageTextView.text, userCountryCode: userCountryCode, userCountryEmoji: userCountryEmoji, messageTimestamp: timestamp, userName: userName, userEmailAddress: userEmail, fcmToken: nil, question: nil, recordingUrl: nil)
messagesReference.childByAutoId().setValue(message)
有没有办法将可解码对象转换为字典以便我可以将其存储在 firebase 中?
你不需要它是可解码的。您需要的是能够对其进行编码(Encodable)。因此,首先将您的结构声明为 Codable。编码后,您可以使用 JSONSerialization jsonObject 方法将数据转换为字典:
extension Encodable {
func data(using encoder: JSONEncoder = .init()) throws -> Data { try encoder.encode(self) }
func string(using encoder: JSONEncoder = .init()) throws -> String { try data(using: encoder).string! }
func dictionary(using encoder: JSONEncoder = .init(), options: JSONSerialization.ReadingOptions = []) throws -> [String: Any] {
try JSONSerialization.jsonObject(with: try data(using: encoder), options: options) as? [String: Any] ?? [:]
}
}
extension Data {
func decodedObject<D: Decodable>(using decoder: JSONDecoder = .init()) throws -> D {
try decoder.decode(D.self, from: self)
}
}
extension Sequence where Element == UInt8 {
var string: String? { String(bytes: self, encoding: .utf8) }
}
我还将 srtuct 属性声明为常量。如果您需要更改任何值,只需创建一个新对象:
struct DiscussionMessage: Codable {
let message, userCountryCode, userCountryEmoji, userName, userEmailAddress: String
let messageTimestamp: Double
let fcmToken, question, recordingUrl: String?
}
let message: DiscussionMessage = .init(message: "message", userCountryCode: "BRA", userCountryEmoji: "", userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: "fcmToken", question: "question", recordingUrl: nil)
do {
let string = try message.string()
print(string) // {"fcmToken":"fcmToken","userName":"userName","message":"message","userCountryEmoji":"","userEmailAddress":"email@address.com","question":"question","messageTimestamp":1610557474.2272739,"userCountryCode":"BRA"}
let dictionary = try message.dictionary()
print(dictionary) // ["userName": userName, "userEmailAddress": email@address.com, "userCountryEmoji": , "messageTimestamp": 1610557474.227274, "question": question, "message": message, "fcmToken": fcmToken, "userCountryCode": BRA]
let data = try message.data() // 218 bytes
let decodedMessages: DiscussionMessage = try data.decodedObject()
print("decodedMessages", decodedMessages) // ecodedMessages DiscussionMessage(message: "message", userCountryCode: "BRA", userCountryEmoji: "", userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: Optional("fcmToken"), question: Optional("question"), recordingUrl: nil)
} catch {
print(error)
}
我要抛出一个答案,看看是否有帮助。
我建议扩展 Encodable 以允许任何符合 return 自身字典的对象可以写入 Firebase。
这是扩展名
extension Encodable {
var dict: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { [=10=] as? [String: Any] }
}
}
然后只需更改结构
struct DiscussionMessage: Encodable {
...your properties...
}
和代码
let msg = DiscussionMessage(data to populate with)
let dict = msg.dict
messagesReference.childByAutoId().setValue(dict)
请记住,实时数据库没有 nil - 任何没有值的节点都不存在,因此只会写入具有值的属性。
哦 - 并确保属性是 NSNumber、NSString、NSDictionary 和 NSArray(和 Bool)的有效 Firebase 类型
我创建了一个可分解的结构,用于解析存储在 firebase 中的数据。
struct DiscussionMessage: Decodable {
var message: String
var userCountryCode: String
var userCountryEmoji: String
var messageTimestamp: Double
var userName: String
var userEmailAddress: String
var fcmToken: String?
var question: String?
var recordingUrl: String?
}
我也想使用这个结构在 firebase 中存储数据。但我得到错误:
*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(setValue:) Cannot store object of type __SwiftValue at . Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.' terminating with uncaught exception of type NSException
当我这样存储数据时:
let message = DiscussionMessage(message: messageTextView.text, userCountryCode: userCountryCode, userCountryEmoji: userCountryEmoji, messageTimestamp: timestamp, userName: userName, userEmailAddress: userEmail, fcmToken: nil, question: nil, recordingUrl: nil)
messagesReference.childByAutoId().setValue(message)
有没有办法将可解码对象转换为字典以便我可以将其存储在 firebase 中?
你不需要它是可解码的。您需要的是能够对其进行编码(Encodable)。因此,首先将您的结构声明为 Codable。编码后,您可以使用 JSONSerialization jsonObject 方法将数据转换为字典:
extension Encodable {
func data(using encoder: JSONEncoder = .init()) throws -> Data { try encoder.encode(self) }
func string(using encoder: JSONEncoder = .init()) throws -> String { try data(using: encoder).string! }
func dictionary(using encoder: JSONEncoder = .init(), options: JSONSerialization.ReadingOptions = []) throws -> [String: Any] {
try JSONSerialization.jsonObject(with: try data(using: encoder), options: options) as? [String: Any] ?? [:]
}
}
extension Data {
func decodedObject<D: Decodable>(using decoder: JSONDecoder = .init()) throws -> D {
try decoder.decode(D.self, from: self)
}
}
extension Sequence where Element == UInt8 {
var string: String? { String(bytes: self, encoding: .utf8) }
}
我还将 srtuct 属性声明为常量。如果您需要更改任何值,只需创建一个新对象:
struct DiscussionMessage: Codable {
let message, userCountryCode, userCountryEmoji, userName, userEmailAddress: String
let messageTimestamp: Double
let fcmToken, question, recordingUrl: String?
}
let message: DiscussionMessage = .init(message: "message", userCountryCode: "BRA", userCountryEmoji: "", userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: "fcmToken", question: "question", recordingUrl: nil)
do {
let string = try message.string()
print(string) // {"fcmToken":"fcmToken","userName":"userName","message":"message","userCountryEmoji":"","userEmailAddress":"email@address.com","question":"question","messageTimestamp":1610557474.2272739,"userCountryCode":"BRA"}
let dictionary = try message.dictionary()
print(dictionary) // ["userName": userName, "userEmailAddress": email@address.com, "userCountryEmoji": , "messageTimestamp": 1610557474.227274, "question": question, "message": message, "fcmToken": fcmToken, "userCountryCode": BRA]
let data = try message.data() // 218 bytes
let decodedMessages: DiscussionMessage = try data.decodedObject()
print("decodedMessages", decodedMessages) // ecodedMessages DiscussionMessage(message: "message", userCountryCode: "BRA", userCountryEmoji: "", userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: Optional("fcmToken"), question: Optional("question"), recordingUrl: nil)
} catch {
print(error)
}
我要抛出一个答案,看看是否有帮助。
我建议扩展 Encodable 以允许任何符合 return 自身字典的对象可以写入 Firebase。
这是扩展名
extension Encodable {
var dict: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { [=10=] as? [String: Any] }
}
}
然后只需更改结构
struct DiscussionMessage: Encodable {
...your properties...
}
和代码
let msg = DiscussionMessage(data to populate with)
let dict = msg.dict
messagesReference.childByAutoId().setValue(dict)
请记住,实时数据库没有 nil - 任何没有值的节点都不存在,因此只会写入具有值的属性。
哦 - 并确保属性是 NSNumber、NSString、NSDictionary 和 NSArray(和 Bool)的有效 Firebase 类型