Вы можете сделать это так:
Сначала вы объявляете свои типы, соответствующие протоколу Decodable
:
struct Fruit : Decodable {
let type : String
let kind : String
let eatable : Bool
}
struct Tool : Decodable {
let tool : String
let original : String
let crossHead : Bool
enum CodingKeys: String, CodingKey {
case tool = "tool"
case original = "original"
case crossHead = "cross-head"
}
}
Затем вы расширяете Decodable
, чтобы «обратить вспять» использование общего вида:
extension Decodable {
static func decode(data : Data, decoder : JSONDecoder = JSONDecoder()) -> Self? {
return try? decoder.decode(Self.self, from: data)
}
}
Затем вы расширяете JSONDecoder
, чтобы попробовать декодируемые типы среди тех, которые вы хотите проверить:
extension JSONDecoder {
func decode(possibleTypes : [Decodable.Type], from data: Data) -> Any? {
for type in possibleTypes {
if let value = type.decode(data: data, decoder: self) {
return value
}
}
return nil
}
}
И, в конце концов, вы указываете типы, которые хотите попробовать и декодировать:
let decodableTypes : [Decodable.Type] = [Fruit.self, Tool.self]
Затем вы можете использовать его для декодирования вашего JSON:
let jsonString = """
{
"tool": "screwdriver",
"original": "toolBox",
"cross-head": true
}
"""
let jsonData = jsonString.data(using: .utf8)!
let myUnknownObject = JSONDecoder().decode(possibleTypes: decodableTypes, from: jsonData)
И вуаля !!!
Теперь вы можете добавить столько типов, сколько хотите, в ваш decodableTypes
, если они соответствуют протоколу Decodable
.
Это не лучший подход, потому что если у вас много типов, он не будет оптимальным, но в этом случае вам не нужно добавлять различающее поле в ваши данные.