Проблема в вашем конкретном примере заключается в том, что тип ваших значений постоянно меняется: иногда tag
- это строка, иногда целое число.Вам понадобится намного больше, чем ваш необязательный подход;это касается того, присутствует ли что-то, а не имеет ли оно правильный тип.Вам понадобится тип объединения, который может декодировать и представлять строку или целое число, например:
enum Sint : Decodable {
case string(String)
case int(Int)
enum Err : Error { case oops }
init(from decoder: Decoder) throws {
let con = try decoder.singleValueContainer()
if let s = try? con.decode(String.self) {
self = .string(s)
return
}
if let i = try? con.decode(Int.self) {
self = .int(i)
return
}
throw Err.oops
}
}
Используя это, я смог декодировать оба ваших примера, используя один тип структуры Animal:
struct Animal: Decodable {
var tag: Sint
var name: String
var type: Sint
}
let j1 = """
{
"tag": 12,
"name": "Dog",
"type": "TYPE1"
}
"""
let j2 = """
{
"tag": "ANIMAL",
"name": "Dog",
"type": 1
}
"""
let d1 = j1.data(using: .utf8)!
let a1 = try! JSONDecoder().decode(Animal.self, from: d1)
let d2 = j2.data(using: .utf8)!
let a2 = try! JSONDecoder().decode(Animal.self, from: d2)
Хорошо, но теперь допустим, что вы даже не знаете, какими будут ключи.Затем вам нужен тип AnyCodingKey, который может убирать ключи независимо от того, какие они есть, и вместо нескольких свойств у вашего Animal будет единственное свойство - словарь, например:
struct Animal: Decodable {
var d = [String : Sint]()
struct AnyCodingKey : CodingKey {
var stringValue: String
var intValue: Int?
init(_ codingKey: CodingKey) {
self.stringValue = codingKey.stringValue
self.intValue = codingKey.intValue
}
init(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
init(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
init(from decoder: Decoder) throws {
let con = try decoder.container(keyedBy: AnyCodingKey.self)
for key in con.allKeys {
let result = try con.decode(Sint.self, forKey: key)
self.d[key.stringValue] = result
}
}
}
Итак, теперь выможет декодировать что-либо с помощью полностью неизвестных ключей, значение которых может быть строкой или целым числом.Опять же, это прекрасно работает с примерами JSON, которые вы привели.
Обратите внимание, что это обратное тому, что вы изначально просили сделать.Вместо того чтобы использовать имена свойств структуры для генерации ключей, я просто принял любой ключ любого типа и гибко сохранил его в структуре с помощью словаря.Вы могли бы также поместить фасад свойства перед этим словарем, используя новую функцию Swift 4.2 dynamicMemberLookup
.Но это оставлено как упражнение для читателя!