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.