Usando Decodable no Swift 4 com Herança

Deve o uso de inheritance de class quebrar a decodibilidade da class. Por exemplo, o código a seguir

class Server : Codable { var id : Int? } class Development : Server { var name : String? var userId : Int? } var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}" let jsonDecoder = JSONDecoder() let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development print(item.id ?? "id is nil") print(item.name ?? "name is nil") here 

a saída é:

 1 name is nil 

Agora, se eu inverter isso, o nome decodifica, mas o id não.

 class Server { var id : Int? } class Development : Server, Codable { var name : String? var userId : Int? } var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}" let jsonDecoder = JSONDecoder() let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development print(item.id ?? "id is nil") print(item.name ?? "name is nil") 

a saída é:

 id is nil Large Building Development 

E você não pode expressar Codable nas duas classs.

Eu acredito que no caso da inheritance você deve implementar Coding própria. Ou seja, você deve especificar CodingKeys e implementar init(from:) e encode(to:) na superclass e na subclass. De acordo com o vídeo da WWDC (por volta de 49:28, na foto abaixo), você deve chamar super com o super codificador / decodificador.

WWDC 2017 Session 212 Screenshot em 49:28 (Código Fonte)

 required init(from decoder: Decoder) throws { // Get our container for this subclass' coding keys let container = try decoder.container(keyedBy: CodingKeys.self) myVar = try container.decode(MyType.self, forKey: .myVar) // otherVar = ... // Get superDecoder for superclass and call super.init(from:) with it let superDecoder = try container.superDecoder() try super.init(from: superDecoder) } 

O vídeo parece não mostrar o lado da codificação (mas é o container.superEncoder() para o lado encode(to:) ), mas funciona da mesma maneira na sua implementação de encode(to:) . Eu posso confirmar que isso funciona neste caso simples (veja o código do playground abaixo).

Ainda estou lutando com algum comportamento estranho com um modelo muito mais complexo que estou convertendo do NSCoding , que tem vários tipos recém-nesteds (incluindo struct e enum ) que exibem esse comportamento nil inesperado e “não deveria ser” . Apenas esteja ciente de que pode haver casos de borda que envolvam tipos nesteds.

Editar: tipos nesteds parecem funcionar bem no meu playground de teste; Eu agora suspeito de algo errado com classs auto-referenciadas (pense em filhos de nós de tree) com uma coleção de si mesmo que também contém instâncias de várias subclasss dessa class. Um teste de uma class de auto-referência simples decodifica muito bem (ou seja, não há subclasss), então estou concentrando meus esforços no motivo pelo qual o caso de subclasss falha.

Atualização de 25 de junho de 17: acabei arquivando um bug com a Apple sobre isso. rdar: // 32911973 – Infelizmente, um ciclo de codificação / decodificação de uma matriz da Superclass que contém elementos Subclass: Superclass resultará na decodificação de todos os elementos da matriz como Superclass (a subclass ‘ init(from:) nunca é chamada, resultando em perda de dados ou pior).

 //: Fully-Implemented Inheritance class FullSuper: Codable { var id: UUID? init() {} private enum CodingKeys: String, CodingKey { case id } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) } } class FullSub: FullSuper { var string: String? private enum CodingKeys: String, CodingKey { case string } override init() { super.init() } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let superdecoder = try container.superDecoder() try super.init(from: superdecoder) string = try container.decode(String.self, forKey: .string) } override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(string, forKey: .string) let superdecoder = container.superEncoder() try super.encode(to: superdecoder) } } let fullSub = FullSub() fullSub.id = UUID() fullSub.string = "FullSub" let fullEncoder = PropertyListEncoder() let fullData = try fullEncoder.encode(fullSub) let fullDecoder = PropertyListDecoder() let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData) 

Ambas as propriedades super e subclass são restauradas em fullSubDecoded .

Consegui fazer isso funcionar fazendo com que minha class base e subclasss Decodable em conformidade com Decodable vez de Codable . Se eu usasse Codable ele iria travar de maneiras estranhas, como obter um EXC_BAD_ACCESS ao acessar um campo da subclass, mas o depurador poderia exibir todos os valores da subclass sem nenhum problema.

Além disso, passar o superDecoder para a class base em super.init() não funcionou. Acabei de passar o decodificador da subclass para a class base.

Que tal usar o seguinte caminho?

 protocol Parent: Codable { var inheritedProp: Int? {get set} } struct Child: Parent { var inheritedProp: Int? var title: String? enum CodingKeys: String, CodingKey { case inheritedProp = "inherited_prop" case title = "short_title" } } 

Informações adicionais sobre composição: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/

Encontrado este link – Vá para a seção de inheritance

 override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(employeeID, forKey: .employeeID) } 

Para decodificar eu fiz isso:

  required init(from decoder: Decoder) throws { try super.init(from: decoder) let values = try decoder.container(keyedBy: CodingKeys.self) total = try values.decode(Int.self, forKey: .total) } private enum CodingKeys: String, CodingKey { case total } 
    Intereting Posts