Skip to content

Instantly share code, notes, and snippets.

@calebkleveter
Created January 30, 2020 17:30
Show Gist options
  • Save calebkleveter/5b5dbc98c20d661d1056fa6cece8d96c to your computer and use it in GitHub Desktop.
Save calebkleveter/5b5dbc98c20d661d1056fa6cece8d96c to your computer and use it in GitHub Desktop.
protocol DataValue: Codable {
static var type: JSONType { get }
}
struct JSONType: Decodable {
private static var register: [String: JSONType] = [:]
let name: String
let decoder: (inout SingleValueDecodingContainer) throws -> DataValue
let encoder: (inout Encoder, DataValue) throws -> Void
public init<Value>(name: String, value: Value.Type) where Value: DataValue {
self.name = name
self.decoder = { container in try container.decode(Value.self) }
self.encoder = { encoder, value in try value.encode(to: encoder) }
JSONType.register[name] = self
}
init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
guard let name = JSONType.register[value] else {
throw DecodingError.dataCorrupted(
.init(codingPath: decoder.codingPath, debugDescription: "No registered Name for id `\(value)`")
)
}
self = name
}
}
struct Experiments: Codable {
let id: Int
let varient: String
let data: [String: DataValue]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.varient = try container.decode(String.self, forKey: .varient)
var elements = try container.nestedUnkeyedContainer(forKey: .data)
var data: [String: DataValue] = [:]
while !elements.isAtEnd {
let element = try elements.nestedContainer(keyedBy: DataKeys.self)
var dataContainer = try element.superDecoder(forKey: .value).singleValueContainer()
let type = try element.decode(JSONType.self, forKey: .type)
let key = try element.decode(String.self, forKey: .key)
data[key] = try type.decoder(&dataContainer)
}
self.data = data
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.varient, forKey: .varient)
var elements = container.nestedUnkeyedContainer(forKey: .data)
try self.data.forEach { key, value in
var element = elements.nestedContainer(keyedBy: DataKeys.self)
let jsonType = type(of: value).type
try element.encode(jsonType.name, forKey: .type)
try element.encode(key, forKey: .key)
try value.encode(to: element.superEncoder(forKey: .value))
}
}
enum CodingKeys: String, CodingKey {
case id, varient, data
}
enum DataKeys: String, CodingKey {
case key, value, type
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment