JSON 可解码,同一标签下有两个结构

Decodable for JSON with two structs under the same tag

我有这个json:

{ "stuff": [
 {
 "type":"car",
 "object":{
  "a":66,
  "b":66,
  "c":66 }},
 {
 "type":"house",
 "object":{
  "d":66,
  "e":66,
  "f":66 }},
 {
 "type":"car",
 "object":{
  "a":66,
  "b":66,
  "c":66 }}
]}

如您所见,“car”和“house”有不同 "object" 结构,但都在标签 "object".

如果最后得到类似

的东西就太理想了
struct StuffItem: Decodable {       
  let type: TheType
  let car: Car
  let house: House
}

是否有一些可编码的、快速的方法来处理这个问题?

在我看来,最快捷 方式是具有关联类型的枚举

这个有效JSON

let jsonString = """
{ "stuff": [
    {
    "type":"car",
    "object":{
        "a":66,
        "b":66,
        "c":66
        }
    },{
    "type":"house",
    "object":{
        "d":66,
        "e":66,
        "f":66
        }
    },{
    "type":"car",
    "object":{
        "a":66,
        "b":66,
        "c":66
        }
    }
]}
"""

这些是结构

struct Root : Decodable {
    let stuff : [Object]
}

enum Type : String, Decodable { case car, house }

struct Car : Decodable {
    let a, b, c : Int
}

struct House : Decodable {
    let d, e, f : Int
}


enum Object : Decodable {
    case house(House), car(Car)

    private enum CodingKeys : String, CodingKey { case type, object }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(Type.self, forKey: .type)
        switch type {
        case .car:
            let carData = try container.decode(Car.self, forKey: .object)
            self = .car(carData)
        case .house:
            let houseData = try container.decode(House.self, forKey: .object)
            self = .house(houseData)
        }
    }
}

以及解码 JSON

的代码
do {
    let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
    let objects = result.stuff
    for object in objects {
        switch object {
        case .car(let car): print(car)
        case .house(let house): print(house)
        }
    }
} catch {
    print(error)
}

您可以通过使用枚举来处理多种情况,只需定义您的类型,提供代码将帮助您通过使用带有枚举的 Struct 模态来解析 JSON。

// MARK: - Welcome
struct Welcome: Codable {
    let stuff: [Stuff]
}

// MARK: - Stuff
struct Stuff: Codable {
    let type: String
    let object: Object
}

// MARK: - Object
struct Object: Codable {
    let a, b, c, d: Int?
    let e, f: Int?
}

enum Type: String {
    case car
    case house
}

func fetchResponse() {
    do {
        let jsonString = "your json string"
        let data = Data(jsonString.utf8)
        let result = try JSONDecoder().decode(Welcome.self, from: data)
        let objects = result.stuff
        let carObjects = objects.filter{[=10=].type == Type.car.rawValue}
        print("Its car array: \(carObjects)")// if you need filters car object then use this
        let houseObjects = objects.filter{[=10=].type == Type.house.rawValue}// if you need filters house object then use this
        print("Its house array: \(houseObjects)")
        // or you check in loop also
        objects.forEach { (stuff) in
            switch stuff.type {
            case Type.car.rawValue:
                print("Its car object")
            case Type.house.rawValue:
                print("Its house object")
            default:
                print("Also you can set your one case in `default`")
                break
            }
        }
    } catch {
        print(error.localizedDescription)
    }
}

另一个解释:

由于对此的解释不多,这是@vadian 解释的另一个例子:

1。在Swift、中使用enum代替struct实现'flexible type'.

2。然后,您需要 'item' 和 'type'.

的解析器

2。解析,然后切换到解码,并设置正确的类型。

所以对于上面的JSON,你有

struct YourFeed: Decodable {
    let stuff: [Item]
}

每个项目可以是汽车或房子。

struct Car: Decodable { ... }
struct House: Decodable { ... }

所以这些很简单。

现在 Item。可以不止一种。

在Swift,使用enum代替struct实现'flexible 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)

接下来,只需将其镜像到原始枚举中,该枚举将用于解析 "type" 字段。 (你可以随便叫它,我只是叫它 "Parse"。)

    // the relevant key strings for parsing the "type" field:
    private enum Parse: String, Decodable {
        case car
        case house
    }

接下来,看原文JSON置顶。每个 "item" 有两个字段,"type" 和 "object"。它们在原始枚举中。 (同样,您可以将其命名为任何名称,我在这里将其命名为 "Keys"。)

    // 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
    }

有一个枚举来解析 'item' 级别,还有一个枚举来解析 'type'。

最后,为"Item"编写初始化程序。只需解码顶层和 "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)

... 大功告成。解码数据(使用相关的 class),并将 "Item" 枚举对象设置为适当的类型。

解析那些,然后切换到解码/设置枚举。

        // 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)
        }
    }
}

这是一口气完成的:

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)
        }
    }
}