Ваше открытие здесь несколько уродливой банки с червями. Я понимаю, что вы пытаетесь сделать, но, к сожалению, это не удается несколькими способами. Вы можете получить немного ближе к тому, что вы хотите, со следующей площадкой:
import Cocoa
let dogData = """
{
"name": "fleabag",
"age": 3,
"type": "big"
}
""".data(using: .utf8)!
let catData = """
{
"name": "felix",
"age": 2,
"color": "black"
}
""".data(using: .utf8)!
protocol Animal: Codable
{
var name: String { get }
var age: Int { get }
}
struct Dog: Animal
{
let name: String
let age: Int
let type: String
}
struct Cat: Animal
{
let name: String
let age: Int
let color: String
}
do {
let decoder = JSONDecoder()
let dog = try decoder.decode(Dog.self, from: dogData)
print(dog)
let cat = try decoder.decode(Cat.self, from: catData)
print(cat)
}
extension Animal {
static func make(fromJSON data: Data) -> Animal? {
let decoder = JSONDecoder()
do {
let dog = try decoder.decode(Dog.self, from: data)
return dog
} catch {
do {
let cat = try decoder.decode(Cat.self, from: data)
return cat
} catch {
return nil
}
}
}
}
if let animal = Dog.make(fromJSON: dogData) {
print(animal)
}
if let animal2 = Dog.make(fromJSON: catData) {
print(animal2)
}
Однако вы заметите, что есть некоторые изменения, у которых есть причина. На самом деле вы не можете реализовать метод Decodable
init(from: Decoder) throws
, поскольку он должен chain
к методу init
, который ... на самом деле не работает для протокола. Вместо этого я решил внедрить ваш любимый диспетчер в методе Animal.make
, но это также оказалось полуобожженным решением. Поскольку protocols
являются метатипами (вероятно, и по уважительной причине), вы не способны вызывать их статические методы для метатипа и должны использовать конкретный. Как показывает линия Dog.make(fromJSON: catData)
, это выглядит странно, если не сказать больше. Было бы лучше испечь это в функцию верхнего уровня, такую как
func parseAnimal(from data:Data) {
...
}
но все равно это выглядит неудовлетворительно с другой стороны, поскольку оно загрязняет глобальное пространство имен. Вероятно, все же лучшее, что мы можем сделать с доступными средствами.
Учитывая уродство диспетчера, кажется плохой идеей иметь JSON без прямого указания типа, поскольку это делает синтаксический анализ действительно трудным. Тем не менее, я не вижу хорошего способа передачи подтипа в JSON таким способом, который действительно облегчает анализ. Не проводил никаких исследований по этому вопросу, но это может быть ваша следующая попытка.