加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

如何在Swift 4中实现JSON数据的多态解码?

发布时间:2020-12-14 04:38:38 所属栏目:百科 来源:网络整理
导读:我试图从API端点返回的数据中呈现视图.我的 JSON看起来(大致)像这样: { "sections": [ { "title": "Featured","section_layout_type": "featured_panels","section_items": [ { "item_type": "foo","id": 3,"title": "Bisbee1","audio_url": "http://exampl
我试图从API端点返回的数据中呈现视图.我的 JSON看起来(大致)像这样:

{
  "sections": [
    {
      "title": "Featured","section_layout_type": "featured_panels","section_items": [
        {
          "item_type": "foo","id": 3,"title": "Bisbee1","audio_url": "http://example.com/foo1.mp3","feature_image_url" : "http://example.com/feature1.jpg"
        },{
          "item_type": "bar","id": 4,"title": "Mortar8","video_url": "http://example.com/video.mp4","director" : "John Smith","feature_image_url" : "http://example.com/feature2.jpg"
        }
      ]
    }    
  ]
}

我有一个对象,表示如何在我的UI中布局视图.它看起来像这样:

public struct ViewLayoutSection : Codable {
    var title: String = ""
    var sectionLayoutType: String
    var sectionItems: [ViewLayoutSectionItemable] = []
}

ViewLayoutSectionItemable是一种协议,其中包括标题和要在布局中使用的图像的URL等.

但是,sectionItems数组实际上由不同类型组成.我想要做的是将每个section项实例化为它自己的类的一个实例.

如何为ViewLayoutSection设置init(来自解码器:解码器)方法,让我迭代该JSON数组中的项并在每种情况下创建正确类的实例?

解决方法

我建议你明智地使用Codable.如果您只想从JSON解码类型而不对其进行编码,那么仅将其与Decodable相符就足够了.既然你已经发现你需要手动解码它(通过自定义的init实现(来自解码器:解码器)),那么问题就变成了:最不痛苦的方法是什么?

一,数据模型.请注意,ViewLayoutSectionItemable及其采用者不符合Decodable:

enum ItemType: String,Decodable {
    case foo
    case bar
}

protocol ViewLayoutSectionItemable {
    var id: Int { get }
    var itemType: ItemType { get }
    var title: String { get set }
    var imageURL: URL { get set }
}

struct Foo: ViewLayoutSectionItemable {
    let id: Int
    let itemType: ItemType
    var title: String
    var imageURL: URL
    // Custom properties of Foo
    var audioURL: URL
}

struct Bar: ViewLayoutSectionItemable {
    let id: Int
    let itemType: ItemType
    var title: String
    var imageURL: URL
    // Custom properties of Bar
    var videoURL: URL
    var director: String
}

接下来,我们将如何解码JSON:

struct Sections: Decodable {
    var sections: [ViewLayoutSection]
}

struct ViewLayoutSection: Decodable {
    var title: String = ""
    var sectionLayoutType: String
    var sectionItems: [ViewLayoutSectionItemable] = []

    // This struct use snake_case to match the JSON so we don't have to provide a custom
    // CodingKeys enum. And since it's private,outside code will never see it
    private struct GenericItem: Decodable {
        let id: Int
        let item_type: ItemType
        var title: String
        var feature_image_url: URL
        // Custom properties of all possible types. Note that they are all optionals
        var audio_url: URL?
        var video_url: URL?
        var director: String?
    }

    private enum CodingKeys: String,CodingKey {
        case title
        case sectionLayoutType = "section_layout_type"
        case sectionItems = "section_items"
    }

    public init(from decoder: Decoder) throws {
        let container     = try decoder.container(keyedBy: CodingKeys.self)
        title             = try container.decode(String.self,forKey: .title)
        sectionLayoutType = try container.decode(String.self,forKey: .sectionLayoutType)
        sectionItems      = try container.decode([GenericItem].self,forKey: .sectionItems).map { item in
        switch item.item_type {
        case .foo:
            // It's OK to force unwrap here because we already
            // know what type the item object is
            return Foo(id: item.id,itemType: item.item_type,title: item.title,imageURL: item.feature_image_url,audioURL: item.audio_url!)
        case .bar:
            return Bar(id: item.id,videoURL: item.video_url!,director: item.director!)
        }
    }
}

用法:

let sections = try JSONDecoder().decode(Sections.self,from: json).sections

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读