1
votes

Hey I have this Json for Example:

{"v":{"value1":1548303671,"value2":"invalid","value3":"invalid"}}

And the model class:

struct VersionApi: Decodable {
    let data: NestedData?
    let v: [String:Int?]

    enum CodingKeys: String, CodingKey {
        case data = "data"
        case v = "v"
    }
}

When trying to decode I get this error message:

debugDescription: "Expected to decode Int but found a string/data instead.", underlyingError: nil)

I know what it means but I don't know a solution. I need a dictionary with Ints in this format: [String:Int?]. I tried to write a custom initializer like that:

init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        data = try values.decode(NestedData, forKey: .data)
        let vID: [String:Any] = try values.decode([String:Any].self, forKey: .v)
        v = try values.decode([String:Int?].self, forKey: .v)

    }

I then wanted to go through the dictionary and if Any is not Int I wanted it to set to nil. But this does not work as no candidates produce the expected type:

No 'decode' candidates produce the expected contextual result type '[String : Any]'

How can I set Int to nil if the value is not an Int?

1
You have to implement codable @PaFiAbhishek Jadhav
Make it String from Int @PaFiAbhishek Jadhav
Right now its A String but a Solution where anything can be there for example also nil would be really nice. Thanks for helping!PaFi
@abhishek what do you mean? The result should be Int?PaFi
Make it String it works, It looks like an Int when you try to decode it found its string that's why and try to implement Codable protocol because it's wrong what you implemented in struct.Abhishek Jadhav

1 Answers

0
votes

As Any is not decodable so compiler is complaining. You can first create an enum (from this answer) as below to decode/encode dynamic type,

enum BiType<L: Codable, R: Codable>: Codable {
    case left(L)
    case right(R)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let left = try? container.decode(L.self) {
            self = .left(left)
        } else if let right = try? container.decode(R.self) {
            self = .right(right)
        } else {
            throw DecodingError
                .typeMismatch(
                    BiType<L, R>.self,
                    .init(codingPath: decoder.codingPath,
                          debugDescription: "Expected either `\(L.self)` or `\(R.self)`"))
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case let .left(left):
            try container.encode(left)
        case let .right(right):
            try container.encode(right)
        }
    }
}

Now you can update VersionApi to use that type,

struct VersionApi: Decodable {
    let data: NestedData?
    let v: [String: BiType<String, Int>]

    enum CodingKeys: String, CodingKey {
        case data = "data"
        case v = "v"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        data = try values.decode(NestedData.self, forKey: .data)
        v = try values.decode([String: BiType<String, Int>].self, forKey: .v)
    }
}

Example

let data = """
{"v":{"value1":1548303671,"value2":"invalid","value3":"invalid"}}
""".data(using: .utf8)!

do {
    let v = try JSONDecoder().decode(VersionApi.self, from: data)
    v.v.values.forEach({ (type) in
        switch type {
        case .left(let left):
            debugPrint(left)
        case .right(let right):
            debugPrint(right)
        }
    })
} catch {
    debugPrint(error)
}

Output

1548303671 
"invalid" 
"invalid"