栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

使用JSONEncoder编码/解码类型符合协议的数组

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用JSONEncoder编码/解码类型符合协议的数组

您的第一个示例无法编译(第二次崩溃)的原因是因为协议不符合自身

不是符合

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")


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/413196.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号