I have this json:
{ \"stuff\": [
{
\"type\":\"car\",
\"object\":{
\"a\":66,
\"b\":66,
\"c\":66 }},
{
\"type\":\"house\",
\"object\":{
\"d\":66,
\
Since there are not many explanations of this around, here's another example of what @vadian has explained:
So for the JSON above, you'd have
struct YourFeed: Decodable {
let stuff: [Item]
}
Each item can be a Car or a House.
struct Car: Decodable { ... }
struct House: Decodable { ... }
So those are easy.
Now for Item
. It can be more than one type.
// in Swift, an "enum" is basically a "struct" which can have a flexible type,
// so here we have enum Item rather than struct Item:
enum Item: Decodable {
// so this thing, Item, can be one of these two types:
case car(Car)
case house(House)
Next, simply mirror that in a raw enum which will be used for parsing the "type" field. (You can call it anything, I've just called it "Parse".)
// the relevant key strings for parsing the "type" field:
private enum Parse: String, Decodable {
case car
case house
}
Next, look at the original JSON up top. Each "item" has two fields, "type" and "object". Here they are in a raw enum. (Again you can call it anything, I've just called it "Keys" here.)
// we're decoding an item, what are the top-level tags in item?
private enum Keys: String, CodingKey {
// so, these are just the two fields in item from the json
case type
case object
}
Finally, write the initializer for "Item". Simply decode the both the top level and the "type" ...
init(from decoder: Decoder) throws {
// parse the top level
let c = try decoder.container(keyedBy: Keys.self)
// and parse the 'type' field
let t = try c.decode(Parse.self, forKey: .type)
... and you're done. Decode the data (using the relevant class), and set the "Item" enum object to the appropriate type.
// we're done, so depending on which of the types it is,
// decode (using the relevant decoder), and become the relevant type:
switch t {
case .car:
let d = try c.decode(Car.self, forKey: .object)
self = .car(d)
case .house:
let d = try c.decode(House.self, forKey: .object)
self = .house(d)
}
}
}
Here's the whole thing in one go:
enum Item: Decodable {
case car(Car)
case house(House)
// the relevant key strings for parsing the 'type' field:
private enum Parse: String, Decodable {
case car
case house
}
// the top-level tags in 'item':
private enum Keys: String, CodingKey {
case type
case object
}
init(from decoder: Decoder) throws {
// parse the top level
let c = try decoder.container(keyedBy: Keys.self)
// parse the 'type' field
let t = try c.decode(Parse.self, forKey: .type)
// we're done, switch to
// decode (using the relevant decoder), and become the relevant type:
switch t {
case .car:
let d = try c.decode(Car.self, forKey: .object)
self = .car(d)
case .house:
let d = try c.decode(House.self, forKey: .object)
self = .house(d)
}
}
}