Я работаю над реализацией 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
).