sebsto
(Sébastien Stormacq)
1
I have an error due to my usage of protocols and generic that I don't understand. Any help appreciated.
Context:
I'm writing code to decode a JSON doc whose format can differ based on a version field.
Example
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": ...,
or
"$schema": "http://json-schema.org/2020_12/schema#",
"$defs": ...,
I wrote a protocol and two struct to support the two dialects.
protocol JSONSchemaDialect {
func codingKeys() -> CodingKey.Type
}
struct JSONSchemaDialect_2020_12: JSONSchemaDialect {
// standard coding keys
enum CodingKeys: String, CodingKey {
case description = "$defs"
}
func codingKeys() -> CodingKey.Type {
return CodingKeys.self
}
}
typealias JSONSchemaDialect_2019_09 = JSONSchemaDialect_2020_12
struct JSONSchemaDialect_Draft4: JSONSchemaDialect {
// Draft4 coding keys
enum CodingKeys: String, CodingKey {
case description = "definitions"
}
func codingKeys() -> CodingKey.Type {
return CodingKeys.self
}
}
and then I have a generic function to decode either schema.
private func decodeDescription<T: JSONSchemaDialect>(from decoder: any Decoder, for dialect: T) throws -> [String: JSONUnionType] {
// Cannot convert value of type 'any CodingKey.Type' to expected argument type 'Key.Type'
// Generic parameter 'Key' could not be inferred
let container = try decoder.container(keyedBy: dialect.codingKeys())
return try container.decodeIfPresent([String: JSONUnionType].self, forKey: .definitions)
}
How can I implement this in a generic way ?
sebsto
(Sébastien Stormacq)
2
Here is a partial solution. Using associatedtype allows me to go one step further. But I'm still blocked. The error is
Type 'T.CodingKeys' has no member 'definitions'
protocol JSONSchemaDialect {
associatedtype CodingKeys: CodingKey
init()
func codingKeys() -> CodingKeys.Type
}
struct JSONSchemaDialect_2020_12: JSONSchemaDialect {
// standard coding keys
enum CodingKeys: String, CodingKey {
case definitions = "$defs"
}
func codingKeys() -> CodingKeys.Type {
return CodingKeys.self
}
}
struct JSONSchemaDialect_Draft4: JSONSchemaDialect {
// Draft4 coding keys
enum CodingKeys: String, CodingKey {
case definitions = "definitions"
}
func codingKeys() -> CodingKeys.Type {
return CodingKeys.self
}
}
and here is how I use this is a generic struct
let dialect: T = .init()
let container = try decoder.container(keyedBy: dialect.codingKeys()) // Works now !
// compiler error : Type 'T.CodingKeys' has no member 'definitions'
self.definitions = try container.decodeIfPresent([String: JSONUnionType].self, forKey: .definitions)