使用 Codable 和 Firestore 的非对称 Encoding/Decoding?
Asymmetric Encoding/Decoding with Codable and Firestore?
我有以下结构:
struct Recipe: Codable {
@DocumentID var id: String?
var vegetarian: Bool?
private enum CodingKeys: String, CodingKey {
case id
case vegetarian
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(vegetarian, forKey: .vegetarian)
}
}
我正在尝试:
- 仅解码
vegetarian
- 编码
vegetarian
和 id
这是我的数据模型:
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.recipe = try document.data(as: Recipe.self)
let recipeFromFirestore = Recipe(
id: self.recipe!.id,
vegetarian: self.recipe!.vegetarian)
self.recipes.append(recipeFromFirestore)
}
catch {
print("Error: \(error)")
}
}
}
}
我的 getDocument
中出现以下错误:Missing argument for parameter 'from' in call
。
如果我在结构中注释掉 init(from decoder: Decoder)
和 func encode(to encoder: Encoder)
,则不会发生此错误。我应该为这种不对称 encoding/decoding 做些不同的事情吗?
我不明白你是从哪里看到这个错误的 - 行参考会很有用 - 但它与你的 en/decoding 没有直接关系。 (顺便说一句,该方法确实不是数据模型)
因为你想用匹配它们 属性 名称的 JSON 键来解码这两个属性,所以不需要指定 CodingKeys
或编写自定义解码器;您可以依靠合成解码器,让 Codable
为您完成工作。
对于解码,您将需要一个自定义解决方案,否则 Decodable
将解码这两个字段。这将需要一个 CodingKey
枚举(注意单数,即协议,而不是默认的 enun 名称)和一个自定义编码器来使用它。
你最终得到了一个更简单的结构实现。我还添加了一个简单的初始化程序,因为一旦定义 init(from:)
,您就会丢失合成的成员初始化程序。这只是为了让我可以测试一下。
struct Recipe: Codable {
var id: String?
var vegetarian: Bool?
init(id: String, vegetarian: Bool){
self.id = id
self.vegetarian = vegetarian
}
init(from decoder: Decoder) throws {
enum DecodingKeys: CodingKey {
case vegetarian
}
let container = try decoder.container(keyedBy: DecodingKeys.self)
vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
}
}
如果您对此进行测试,您会发现它只会对 vegetarian
属性 进行解码,但会对两者进行编码。简单测试显示:
let recipe = Recipe(id: "1", vegetarian: true)
let data = try! JSONEncoder().encode(recipe)
print(String(data: data, encoding: .utf8)!) //{"id":"1","vegetarian":true}
let decodedRecipe = try! JSONDecoder().decode(Recipe.self, from: data)
print(decodedRecipe) //Recipe(id: nil, vegetarian: Optional(true))
我有以下结构:
struct Recipe: Codable {
@DocumentID var id: String?
var vegetarian: Bool?
private enum CodingKeys: String, CodingKey {
case id
case vegetarian
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(vegetarian, forKey: .vegetarian)
}
}
我正在尝试:
- 仅解码
vegetarian
- 编码
vegetarian
和id
这是我的数据模型:
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.recipe = try document.data(as: Recipe.self)
let recipeFromFirestore = Recipe(
id: self.recipe!.id,
vegetarian: self.recipe!.vegetarian)
self.recipes.append(recipeFromFirestore)
}
catch {
print("Error: \(error)")
}
}
}
}
我的 getDocument
中出现以下错误:Missing argument for parameter 'from' in call
。
如果我在结构中注释掉 init(from decoder: Decoder)
和 func encode(to encoder: Encoder)
,则不会发生此错误。我应该为这种不对称 encoding/decoding 做些不同的事情吗?
我不明白你是从哪里看到这个错误的 - 行参考会很有用 - 但它与你的 en/decoding 没有直接关系。 (顺便说一句,该方法确实不是数据模型)
因为你想用匹配它们 属性 名称的 JSON 键来解码这两个属性,所以不需要指定 CodingKeys
或编写自定义解码器;您可以依靠合成解码器,让 Codable
为您完成工作。
对于解码,您将需要一个自定义解决方案,否则 Decodable
将解码这两个字段。这将需要一个 CodingKey
枚举(注意单数,即协议,而不是默认的 enun 名称)和一个自定义编码器来使用它。
你最终得到了一个更简单的结构实现。我还添加了一个简单的初始化程序,因为一旦定义 init(from:)
,您就会丢失合成的成员初始化程序。这只是为了让我可以测试一下。
struct Recipe: Codable {
var id: String?
var vegetarian: Bool?
init(id: String, vegetarian: Bool){
self.id = id
self.vegetarian = vegetarian
}
init(from decoder: Decoder) throws {
enum DecodingKeys: CodingKey {
case vegetarian
}
let container = try decoder.container(keyedBy: DecodingKeys.self)
vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
}
}
如果您对此进行测试,您会发现它只会对 vegetarian
属性 进行解码,但会对两者进行编码。简单测试显示:
let recipe = Recipe(id: "1", vegetarian: true)
let data = try! JSONEncoder().encode(recipe)
print(String(data: data, encoding: .utf8)!) //{"id":"1","vegetarian":true}
let decodedRecipe = try! JSONDecoder().decode(Recipe.self, from: data)
print(decodedRecipe) //Recipe(id: nil, vegetarian: Optional(true))