Вы всегда можете использовать гигантский оператор switch, проверяющий каждое возможное значение (ew), но если вы собираетесь это сделать, гораздо лучшим способом будет использование стирания типа. Недостатком является то, что вы в значительной степени должны пройти через go все ваши возможные классы и объявляйте их как соответствующие или реализующие забавный c, который возвращает декодируемый тип - который может быть громоздким.
В идеале мы могли бы просто получить «готовую» поддержку для всего, что Decodable
.
Для этого нам потребуется языковая функция, называемая открытием экзистенциалов - есть отличная и подробная запись. проблем, с которыми вы сталкиваетесь в этом вопросе ( Протокол не соответствует самому себе? )
В любом случае, понятия не имею, работает ли это, но я был в состоянии получить что-то для компиляции этого кажется, что делает то, что вы хотите:
class AnyDecodableWrapper : Decodable {
static let decodableTypesLookup: [String: Decodable] = [
"str": "",
"int": 0
]
enum CodingKeys : String, CodingKey {
case typeKey
case value
}
private struct DecodableObject: Decodable {}
required init(from decoder: Decoder) throws {
// Get the container for the CodingKeys
let container = try decoder.container(keyedBy: CodingKeys.self)
typeKey = try container.decode(String.self, forKey:.typeKey)
guard let concreteType = AnyDecodableWrapper.decodableTypesLookup[typeKey] else {
value = nil
return
}
let concreteObject: DecodableObject = try container.unambiguousDecode(concreteType, forKey: .value)
value = concreteObject
}
let typeKey : String
let value : Any?
}
extension KeyedDecodingContainer {
func unambiguousDecode<T: Decodable>(_ decodableType: Decodable, forKey key: KeyedDecodingContainer<K>.Key) throws -> T {
let decodable = type(of: decodableType) as! T.Type
return try decode(decodable, forKey: key)
}
}
Объяснение:
Проблема в том, что компилятор не может определить, какой из 16 decode:
методов использовать в KeyedDecodingContainer
.
Поскольку у swift нет открытых экзистенциалов, он не может сказать, что передача аргумента Decodable.Type
такая же, как T.Type where T: Decodable
Итак, первое, что я сделал, - это работа с Decodable
напрямую вместо Decodable.Type
. Это означало, что нужно изменить decodableTypesLookup
на [String: Decodable]
и создать собственный метод декодирования для KeyedDecodingContainer
, в котором вместо мета-типа будет использоваться Decodable
(как в стандартном decode:
fun c take).
Здесь меня беспокоит следующая строка:
let decodable = type(of: decodableType) as! T.Type
Если это не так, возможно, есть способ привести к обобщенному типу c вместо использования as!
?
Так или иначе, из-за этого у меня произошла ошибка (Generi c параметр T не может быть выведен) на
let concreteObject = try container.unambiguousDecode(concreteType, forKey: .value)
, и это только потому, что вы не можете получить конкретный бетон Decodable()
и он понятия не имеет, каким должен быть конкретный объект.
Чтобы обойти это, я просто создал одноразовый выброс:
private struct DecodableObject: Decodable {}
Просто чтобы я мог сказать компилятору, что будь конкретным объектом:
let concreteObject: DecodableObject = try container.unambiguousDecode(concreteType, forKey: .value)
тогда мы можем явно скрыть это ожидаемым типом Any?
(возможно, AnyObject
будет лучше tbh)
Надеюсь, что это работает ..