rundfunk47 / Juice

A modern and simple JSON Encoder / Decoder for Swift 3
MIT License
6 stars 1 forks source link

Exception from deep within when decoding a derived class inside an object #3

Open jkufver opened 6 years ago

jkufver commented 6 years ago

Hi thanks for the updated version 1.0.9.

I just stumbled upon another issue with Juice.

I have a class B which inherits from a super class A:

class A: Juice.Decodable {
    var a: String?

    required init(fromJson json: Juice.JSONDictionary) throws {
        a = try? json.decode(["a"])
    }
}

class B: A {
    var b: String?

    required init(fromJson json: Juice.JSONDictionary) throws {
        b = try? json.decode(["b"])
        try super.init(fromJson: json)
    }
}

When I decode B (in my case an array of B's (not shown in this reduced example)) I get an exception from deep inside of Juice.

Example code:

        // Decoding simple object B -> Works
        do {
            let string = "{\"a\": \"Apple 0\", \"b\": \"Brother 0\"}"
            let dict = try toStrictlyTypedJSON(toLooselyTypedJSON(string)) as! JSONDictionary
            let o = try B(fromJson: dict)
            print(o.a, o.b)
        } catch {
            print(error.localizedDescription)
        }

        // B as a part of an object -> Fails
        do {
            let string = "{\"object\": {\"a\": \"Apple 1\", \"b\": \"Brother 1\"}}"
            let dict = try toStrictlyTypedJSON(toLooselyTypedJSON(string)) as! JSONDictionary

            // This works
            let oa: A = try dict.decode("object")
            print(oa.a)

            // This fails with exception "Could not cast value of type 'A' to 'B'"
            let ob: B = try dict.decode("object")
            print(ob.a, ob.b)
        } catch {
            print(error.localizedDescription)
        }

My knowledge of generics is a bit to shallow to find the reason.

All the best

rundfunk47 commented 6 years ago

Hmm, that's interesting. Will require a closer look, but in the mean time, this workaround seems to work for me:

let ob: B = try dict.decode("object") { (dict: JSONDictionary) in
        return try B(fromJson: dict)
}
jkufver commented 6 years ago

Hi thanks for the feedback.

I solved my urgent need by not using a derived class.

Your suggestion would not solve my need since in my case B is a part of a another object.

class C: Juice.Decodable {
    let bs: [B]
   …
}

and to decode C and all Bs the dict.decode(…) method must work. At least I think so...

jkufver commented 6 years ago

Today I understood what you wrote two days ago. Sorry about that...

With the transform functionality I can now parse an array of Bs:

let arr: [B] = try dict.decode("arr") { (arr: JSONArray) in
    try arr.map {
        guard let dict = $0 as? JSONDictionary else {
            throw MismatchError.typeMismatch(expected: JSONDictionary.self, got: $0)
        }
        return try B(fromJson: dict)
    }
}