Я работаю над реализацией Codable
для типа enum
с возможными связанными значениями. Поскольку они уникальны для каждого случая, я подумал, что смогу обойтись без вывода их во время кодирования, а затем просто посмотреть, что я могу получить при декодировании, чтобы восстановить правильный регистр.
Вот очень урезанный, надуманный пример, демонстрирующий своего рода динамически типизированное значение:
enum MyValueError : Error { case invalidEncoding }
enum MyValue {
case bool(Bool)
case float(Float)
case integer(Int)
case string(String)
}
extension MyValue : Codable {
init(from theDecoder:Decoder) throws {
let theEncodedValue = try theDecoder.singleValueContainer()
if let theValue = try? theEncodedValue.decode(Bool.self) {
self = .bool(theValue)
} else if let theValue = try? theEncodedValue.decode(Float.self) {
self = .float(theValue)
} else if let theValue = try? theEncodedValue.decode(Int.self) {
self = .integer(theValue)
} else if let theValue = try? theEncodedValue.decode(String.self) {
self = .string(theValue)
} else { throw MyValueError.invalidEncoding }
}
func encode(to theEncoder:Encoder) throws {
var theEncodedValue = theEncoder.singleValueContainer()
switch self {
case .bool(let theValue):
try theEncodedValue.encode(theValue)
case .float(let theValue):
try theEncodedValue.encode(theValue)
case .integer(let theValue):
try theEncodedValue.encode(theValue)
case .string(let theValue):
try theEncodedValue.encode(theValue)
}
}
}
let theEncodedValue = try! JSONEncoder().encode(MyValue.integer(123456))
let theEncodedString = String(data: theEncodedValue, encoding: .utf8)
let theDecodedValue = try! JSONDecoder().decode(MyValue.self, from: theEncodedValue)
Однако это дает мне ошибку на этапе кодирования следующим образом:
"Top-level MyValue encoded as number JSON fragment."
Проблема, по-видимому, заключается в том, что по какой-то причине JSONEncoder
не позволяет кодировать тип верхнего уровня, который не является распознанным примитивом, как одно значение примитива. Если я изменю singleValueContainer()
на unkeyedContainer()
, то он будет работать нормально, за исключением того, что, конечно, результирующий JSON
будет массивом, а не отдельным значением, или я могу использовать контейнер с ключом, но это создает объект с дополнительными накладными расходами ключа.
То, что я пытаюсь здесь сделать, невозможно с контейнером с одним значением? Если нет, есть ли обходной путь, который я могу использовать вместо этого?
Моя цель состояла в том, чтобы сделать мой тип Codable
с минимальными накладными расходами, а не только как JSON
(решение должно поддерживать любые допустимые Encoder
/Decoder
).
let theEncodedValue = try! JSONEncoder().encode([MyValue.integer(123456)])
, а затем расшифровать его с помощьюlet theDecodedValue = try! JSONDecoder().decode([MyValue].self, from: theEncodedValue)
- person Guy Kogus   schedule 09.05.2018