Container.decode - run-time vs compile-time type parameter

I'm trying to encode and decode something that can contain objects from a class hierarchy. I encode a string to identify the specific subtype type. Then I use the string to lookup the type instance and try to decode with that. But the call to c.decode does not work. It creates an instance of BaseClass even if the type is one for a subclass (eg, SubClass1.self).

So I guess that call to c.decode ignores it's runtime parameter in favor of the static type parameter? That seems strange. Is there a way to write this code without hardcoding the subclass names. Ie, without having to write c.decode(SubClass1.self, forKey: object)

required init(from decoder: Decoder) throws {
    let c = try decoder.container(keyedBy: CodingKeys.self)
    let typeCode = try c.decode(String.self, forKey: .type)
    let type: BaseClass.Type = getTypeFromCode(typeCode)   
    // let type = SubClass1.self     <-- this works because then type: SubClass1.Type
    self.object = try c.decode(type, forKey: .object)
}

I don't think so. I think it is by design not to decode any class the implementation doesn't know about.

And the thing about c.decode(type, forKey: .object) not working. It is because the call is statically dispatched. Which ever overload used is already decided at compile time, which is only known to be BaseClass.

That's what I was wondering too. But it doesn't actually prevent the decoding. I can write:

self.object = try type.init(with: decoder)

but the problem with that is that it expects the properties to be at this level of the JSON (or whatever is being decoded), instead of under the nested key. Or I can write:

switch typeCode {
    case "sub1":  self.object = try c.decode(Sub1.self, forKey: .object)
    ...

I may just use that, but it seems ugly if there are a lot of subtypes and similar code has to be pasted in multiple places.

You probably can get a nested decoder with c.superDecoder(forKey:) though I wouldn't recommended it.

What's in getTypeFromCode? Isn't it also a big switch case?

A switch or a dictionary lookup. I can copy-paste the switch into init, but not the dictionary lookup. Seems like we should have a c.nestedDecoder(forKey:)

Terms of Service

Privacy Policy

Cookie Policy