我使用协议创建了几个结构,我用它来使用JSONDecoder
进行解码。这是我试图实现的代码示例。
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
}
以下是狗和猫的单独JSON有效负载:
{
"name": "fleabag",
"age": 3,
"type": "big"
}
{
"name": "felix",
"age": 2,
"color": "black"
}
因此,当我解码JSON时,我不确定我将拥有什么JSON,狗或猫。我试过这样做:
let data = Data(contentsOf: url)
let value = JSONDecoder().decode(Animal.self, from: data)
但最终出现了这个错误:
在论证类型' Animal.Protocol',' Animal'不符合预期类型'可解码'
关于解析狗或猫返回Animal
实例的最佳方法的任何想法?
由于
答案 0 :(得分:0)
更好的方法是使用类而不是协议并使用类而不是结构。您的Dog
和Cat
类将是Animal
class Animal: Codable {
let name: String
let age: Int
private enum CodingKeys: String, CodingKey {
case name
case age
}
}
class Dog: Animal {
let type: String
private enum CodingKeys: String, CodingKey {
case type
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.type = try container.decode(String.self, forKey: .type)
try super.init(from: decoder)
}
}
class Cat: Animal {
let color: String
private enum CodingKeys: String, CodingKey {
case color
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.color = try container.decode(String.self, forKey: .color)
try super.init(from: decoder)
}
}
let data = Data(contentsOf: url)
let animal = JSONDecoder().decode(Animal.self, from: data)
答案 1 :(得分:0)
你无法使用它:
let animal = try? JSONDecoder().decode(Animal.self, from: data)
解码狗或猫。它永远都是动物。
如果要将这些JSON对象解码为Animal,请按以下方式定义Animal:
struct Animal: Codable {
var name: String
var age: Int
}
当然,你会失去使他们成为狗(type
)或猫(color
)的独特元素。
答案 2 :(得分:0)
你在这里打开一些有点丑陋的虫子。我理解你尝试做什么,但不幸的是它在很多方面失败了。您可以通过以下游乐场获得您想要的内容:
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中传递子类型的好方法。没有对此进行任何研究,但它可能是你的下一次尝试。