您的第一个示例无法编译(第二次崩溃)的原因是因为协议不符合自身
–
不是符合
Tag的类型
Codable,因此也不符合
[Tag]。因此
Article不会获得自动生成的
Codable一致性,因为并非其所有属性都符合
Codable。
仅对协议中列出的属性进行编码和解码
如果只想对协议中列出的属性进行编码和解码,则一种解决方案是仅使用
AnyTag仅保留这些属性的类型擦除器,然后提供
Codable一致性。
然后,您可以拥有
Article此类型擦除包装器的数组,而不是
Tag:
struct AnyTag : Tag, Codable { let type: String let value: String init(_ base: Tag) { self.type = base.type self.value = base.value }}struct Article: Codable { let tags: [AnyTag] let title: String}let tags: [Tag] = [ AuthorTag(value: "Author Tag Value"), GenreTag(value:"Genre Tag Value")]let article = Article(tags: tags.map(AnyTag.init), title: "Article Title")let jsonEnprer = JSonEnprer()jsonEnprer.outputFormatting = .prettyPrintedlet jsonData = try jsonEnprer.enpre(article)if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString)}输出以下JSON字符串:
{ "title" : "Article Title", "tags" : [ { "type" : "author", "value" : "Author Tag Value" }, { "type" : "genre", "value" : "Genre Tag Value" } ]}可以这样解码:
let depred = try JSonDeprer().depre(Article.self, from: jsonData)print(depred)// Article(tags: [// AnyTag(type: "author", value: "Author Tag Value"),// AnyTag(type: "genre", value: "Genre Tag Value")// ], title: "Article Title")
编码和解码一致类型的所有属性
但是,如果您需要对给定符合类型的 每个 属性进行编码和解码,则
Tag可能需要以某种方式将类型信息存储在JSON中。
我将使用
enum来执行此操作:
enum TagType : String, Codable { // be careful not to rename these – the encoding/decoding relies on the string // values of the cases. If you want the decoding to be reliant on case // position rather than name, then you can change to enum TagType : Int. // (the advantage of the String rawValue is that the JSON is more readable) case author, genre var metatype: Tag.Type { switch self { case .author: return AuthorTag.self case .genre: return GenreTag.self } }}这比仅使用普通字符串表示类型更好,因为编译器可以检查我们是否为每种情况提供了元类型。
然后,您只需要更改
Tag协议,使其需要符合标准的类型即可实现
static描述其类型的属性:
protocol Tag : Codable { static var type: TagType { get } var value: String { get }}struct AuthorTag : Tag { static var type = TagType.author let value: String var foo: Float}struct GenreTag : Tag { static var type = TagType.genre let value: String var baz: String}然后,我们需要调整类型擦除包装器的实现,以便
TagType与base一起编码和解码
Tag:
struct AnyTag : Codable { var base: Tag init(_ base: Tag) { self.base = base } private enum CodingKeys : CodingKey { case type, base } init(from deprer: Deprer) throws { let container = try deprer.container(keyedBy: CodingKeys.self) let type = try container.depre(TagType.self, forKey: .type) self.base = try type.metatype.init(from: container.superDeprer(forKey: .base)) } func enpre(to enprer: Enprer) throws { var container = enprer.container(keyedBy: CodingKeys.self) try container.enpre(type(of: base).type, forKey: .type) try base.enpre(to: container.superEnprer(forKey: .base)) }}我们使用超级编码器/解码器,以确保给定符合类型的属性键不会与用于编码该类型的键冲突。例如,编码的JSON将如下所示:
{ "type" : "author", "base" : { "value" : "Author Tag Value", "foo" : 56.7 }}但是,如果您知道不会有冲突,并且希望在与“类型”键 相同的 级别上对属性进行编码/解码,则JSON如下所示:
{ "type" : "author", "value" : "Author Tag Value", "foo" : 56.7}您可以通过传递
deprer而不是
container.superDeprer(forKey:.base)&
enprer代替
container.superEnprer(forKey: .base)上面的代码。
作为 可选 步骤,我们然后可以自定义
Codable实现
Article,而不是依赖于
tags类型为type
的属性的自动生成的符合性
[AnyTag],我们可以提供自己的实现,将a打包
[Tag]成一个
[AnyTag]before编码,然后将unbox解码:
struct Article { let tags: [Tag] let title: String init(tags: [Tag], title: String) { self.tags = tags self.title = title }}extension Article : Codable { private enum CodingKeys : CodingKey { case tags, title } init(from deprer: Deprer) throws { let container = try deprer.container(keyedBy: CodingKeys.self) self.tags = try container.depre([AnyTag].self, forKey: .tags).map { $0.base } self.title = try container.depre(String.self, forKey: .title) } func enpre(to enprer: Enprer) throws { var container = enprer.container(keyedBy: CodingKeys.self) try container.enpre(tags.map(AnyTag.init), forKey: .tags) try container.enpre(title, forKey: .title) }}然后,这使我们可以将
tags属性的类型设为
[Tag],而不是
[AnyTag]。
现在我们可以对枚举中
Tag列出的任何符合类型进行编码和解码
TagType:
let tags: [Tag] = [ AuthorTag(value: "Author Tag Value", foo: 56.7), GenreTag(value:"Genre Tag Value", baz: "hello world")]let article = Article(tags: tags, title: "Article Title")let jsonEnprer = JSonEnprer()jsonEnprer.outputFormatting = .prettyPrintedlet jsonData = try jsonEnprer.enpre(article)if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString)}输出JSON字符串:
{ "title" : "Article Title", "tags" : [ { "type" : "author", "base" : { "value" : "Author Tag Value", "foo" : 56.7 } }, { "type" : "genre", "base" : { "value" : "Genre Tag Value", "baz" : "hello world" } } ]}然后可以像这样解码:
let depred = try JSonDeprer().depre(Article.self, from: jsonData)print(depred)// Article(tags: [// AuthorTag(value: "Author Tag Value", foo: 56.7000008),// GenreTag(value: "Genre Tag Value", baz: "hello world")// ],// title: "Article Title")



