How to deal with completely dynamic JSON responses

What are you looking to do with the end result of the decoded value? Are you passing that along to another API, or looking to consume that somehow yourself?

JSONEncoder and JSONDecoder currently don't have an answer for totally arbitrary input; one of the core components of Codable is requiring you to know what type you're looking to decode and asking for that. If you're looking to decode one of multiple known types, then Swift's existing solution to a multiple type representation is the way to go — making a Codable enum representing those types, e.g.:

enum StringOrInt : Codable {
    case string(String)
    case int(Int)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = .string(try container.decode(String.self))
        } catch DecodingError.typeMismatch {
            self = .int(try container.decode(Int.self))
        }
    }

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

It's similarly possible to write an enum which can represent all of the underlying JSON types and encoding and decoding that. It's a bit more verbose, but possible.

I've also been interested in adding JSONEncoder.UnevaluatedJSON and JSONDecoder.UnevaluatedJSON, special wrappers around Any which allow you to encode and decode otherwise untouched JSON types (Dictionary, Array, Int, String, etc.). These would be handled specifically by JSONEncoder and JSONDecoder to essentially leave whatever types they contain alone. So, for instance, you'd write

let container = try decoder.singleValueContainer()
self.stuff = try decoder.decode(JSONDecoder.UnevaluatedJSON)
// self.stuff.value now contains whatever was in the container, e.g. Int or String or [String : [Double]].

Of course, it doesn't make sense to ask a different decoder (e.g. PropertyListDecoder) to decode JSONDecoder.UnevaluatedJSON, so this concept would need to be further refined as API. I don't think we'll have time to do this in the Swift 5 timeframe.


In any case, more information about the specifics of what you're trying to do with that polymorphic value would help give a more pointed solution.