为什么此模型不符合“可解码”? (一个多态的JSON圣诞故事)

问题描述 投票:0回答:1

你好Codable专家。

我想解码艺术家数据的传入JSON。存在两种类型的艺术家:个人和乐队。

个人代表如下:

{
  "id":"123",
  "data":{
    "type":"individual",
    "firstName":"David",
    "lastName":"Bowie"
  }
}

而乐队代表这样:

{
  "id":"124",
  "data":{
    "type":"band",
    "name":"Queen"
  }
}

我正在尝试在Swift中对此JSON polymorphism建模(🤔不确定这是正确的词),如下所示:

import Foundation

struct Artist: Decodable {
    let id: String
    let data: ArtistData
}

enum ArtistType: String, Codable {
    case individual
    case band
}

protocol ArtistData: Decodable {
    var type: ArtistType { get }
}

struct IndividualArtistData: ArtistData, Codable {
    let type = ArtistType.individual
    let firstName: String
    let lastName: String
}

struct BandArtistData: ArtistData, Codable {
    let type = ArtistType.band
    let name: String
}

但是出现以下错误:

Type 'Artist' does not conform to protocol 'Decodable'
cannot automatically synthesize 'Decodable' because 'ArtistData' does not conform to 'Decodable'

[我还尝试了一个版本,其中ArtistData协议不继承Decodable并更改Artist定义以使用协议组成,如下所示:

struct Artist: Decodable {
    let id: String
    let data: ArtistData & Decodable
}

但出现类似错误:

cannot automatically synthesize 'Decodable' because 'Decodable & ArtistData' does not conform to 'Decodable'

我应该如何处理这种情况?

节日快乐。🌲🎅

json swift swift-protocols decodable
1个回答
0
投票

data中的[Artist必须是具体类型或限于Codable的泛型。它不能是协议。

我的建议是删除协议,并声明一个具有关联类型的枚举。

enum Artist : Decodable {
    case individual(String, IndividualArtist), band(String, BandArtist)

    private enum CodingKeys : String, CodingKey { case id, data }
    private enum ArtistKeys : String, CodingKey { case type }

    init(from decoder : Decoder) throws
    {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let id = try container.decode(String.self, forKey: .id)
        let nestedContainer = try container.nestedContainer(keyedBy: ArtistKeys.self, forKey: .data)
        let type = try nestedContainer.decode(ArtistType.self, forKey: .type)
        switch type {
            case .individual:
                let individualData = try container.decode(IndividualArtist.self, forKey: .data)
                self = .individual(id, individualData)
            case .band:
                let bandData = try container.decode(BandArtist.self, forKey: .data)
                self = .band(id, bandData)
        }
    }
}

enum ArtistType : String, Decodable {
    case individual
    case band
}

struct IndividualArtist : Decodable {
    let type : ArtistType
    let firstName: String
    let lastName: String
}

struct BandArtist : Decodable {
    let type : ArtistType
    let name: String
}
© www.soinside.com 2019 - 2024. All rights reserved.