0

当我解码一个子类(RegularCard)的数组时,我得到一个超类(Card)的数组。编码很好,我测试了它并且编码工作正常。但是,当我解码时,不会调用解码子类(RegularCard)的函数。我的代码如下。我在另一篇文章中发现了以下错误:

2017 年 6 月 25 日更新:我最终向 Apple 提交了一个关于此的错误。rdar://32911973 - 不幸的是,包含子类的超类数组的编码/解码循环:超类元素将导致数组中的所有元素都被解码为超类(永远不会调用子类的 init(from:),从而导致数据丢失或更糟)。

这是在这篇文章中。

class Card : Codable {

    var id: Int
    var front: String
    var score: Int
    var cardType : CardType
    
    init(id: Int, front : String, score: Int, cardType : CardType) {
        self.id = id
        self.front = front
        self.score = score
        self.cardType = cardType
    }
    
    enum CodingKeys : String, CodingKey {
        case id
        case front
        case score
        case cardType
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        print("encoding super")
        try container.encode(id, forKey: .id)
        try container.encode(front, forKey: .front)
        try container.encode(score, forKey: .score)
        try container.encode(cardType.rawValue, forKey: .cardType)
    }
    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        print("decoding super")
        self.id = try values.decode(Int.self, forKey: .id)
        self.front = try values.decode(String.self, forKey: .front)
        self.score = try values.decode(Int.self, forKey: .score)
        self.cardType = CardType(rawValue: try values.decode(String.self, forKey: .cardType))!
    }
}


enum CardType : String {
    case regular = "regular"
    case multipleChoice = "multipleChoice"
    case numbered = "numbered"
    case bulleted = "bulleted"
    case acronym = "acronym"
    case image = "image"
}

class RegularCard : Card {
    var back: String
    
    init(id: Int, front : String, score: Int, back : String) {
        self.back = back
        super.init(id: id, front: front, score: score, cardType: .regular)
    }
    enum CodingKeys : String, CodingKey {
        case back
        case id
        case front
        case score
        case cardType
    }
    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        print("encoding Regular")
        try super.encode(to: encoder)
        try container.encode(back, forKey: .back)
    }
    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        print("decoding regular")
        back = try values.decode(String.self, forKey: .back)
        try super.init(from: decoder)

    }
}

'''

4

1 回答 1

1

对我来说很好:

    let reg = RegularCard(id: 1, front: "yo", score: 20, back: "frf")
    let arr = [reg]
    let data = try! JSONEncoder().encode(arr)
    let arr2 = try! JSONDecoder().decode([RegularCard].self, from: data)
    print(type(of:arr2)) // Array<RegularCard>
    let what = arr2.first!
    print(what.id, what.front, what.score, what.back) // 1 yo 20 frf

请注意,我确实必须稍微更改您的代码;我将您的第二个 CodingKeys 更改为 CodingKeys2,否则我无法编译您的代码。

class RegularCard : Card {
    var back: String
    
    init(id: Int, front : String, score: Int, back : String) {
        self.back = back
        super.init(id: id, front: front, score: score, cardType: .regular)
    }
    enum CodingKeys2 : String, CodingKey {
        case back
        case id
        case front
        case score
        case cardType
    }
    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys2.self)
        print("encoding Regular")
        try super.encode(to: encoder)
        try container.encode(back, forKey: .back)
    }
    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys2.self)
        print("decoding regular")
        back = try values.decode(String.self, forKey: .back)
        try super.init(from: decoder)
        
    }
}

但是,我确实理解您的观点;即使RegularCard进入,如果我们解码为Card,我们也无法将值恢复为RegularCard(不能向下转换)。我猜这只是 Codable 或泛型的本质。基本上,我的建议是,不要那样做。

但是,在这种情况下,您可以尝试解码为 RegularCard,如果不起作用,则解码为 Card。这样你总能得到正确的答案。

let card = Card(id: 2, front: "hoo", score: 10, cardType: .regular)
let reg = RegularCard(id: 1, front: "yo", score: 20, back: "frf")
let arr1 = [card]
let arr2 = [reg]
let data1 = try! JSONEncoder().encode(arr1)
let data2 = try! JSONEncoder().encode(arr2)

// first data1
do {
    let result1 = try JSONDecoder().decode([RegularCard].self, from: data1)
    print(result1)
} catch {
    let result2 = try JSONDecoder().decode([Card].self, from: data1)
    print(result2) // it's a Card
}

// now data2
do {
    let result1 = try JSONDecoder().decode([RegularCard].self, from: data2)
    print(result1) // it's a RegularCard
} catch {
    let result2 = try JSONDecoder().decode([Card].self, from: data2)
    print(result2)
}
于 2020-09-20T00:31:52.667 回答