Is it possible with the Decodable
protocol in Swift 4 to decode a JSON object when the type to decode to is only known at runtime?
I have a registry of sorts which maps a String
identifier to the type we want to decode to, as below:
import Foundation
struct Person: Decodable {
let forename: String
let surname: String
}
struct Company: Decodable {
let officeCount: Int
let people: [Person]
}
let registry: [String:Decodable.Type] = [
"Person": Person.self,
"Company": Company.self
]
let exampleJSON = """
{
"forename": "Bob",
"surname": "Jones"
}
""".data(using: .utf8)!
let t = registry["Person"]!
try! JSONDecoder().decode(t, from: exampleJSON) // doesn't work :-(
Am I on the right lines here or is there a better way?
Your design is indeed unique but, unfortunately, I believe you are hitting an edge case of Swift's type system. Basically, a protocol doesn't conform to itself and, as such, your general Decodable.Type
isn't enough here (i.e., you really need a concrete type to satisfy the type system requirements). This might explains the error you are having:
Cannot invoke
decode
with an argument list of type(Decodable.Type, from: Data)
. Expected an argument list of type(T.Type, from: Data)
.
But, having said that, there is indeed a (dirty!) hack around this. First, create a dummy DecodableWrapper
to hold your runtime-ish Decodable
type:
struct DecodableWrapper: Decodable {
static var baseType: Decodable.Type!
var base: Decodable
init(from decoder: Decoder) throws {
self.base = try DecodableWrapper.baseType.init(from: decoder)
}
}
then use it like this:
DecodableWrapper.baseType = registry["Person"]!
let person = try! JSONDecoder().decode(DecodableWrapper.self, from: exampleJSON).base
print("person: \(person)")
prints the expected result:
person: Person(forename: "Bob", surname: "Jones")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With