Ios 在同一标记下具有两个结构的JSON可解码

Ios 在同一标记下具有两个结构的JSON可解码,ios,json,swift5,decodable,Ios,Json,Swift5,Decodable,我有一个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,有不同的对象结构,但都在

我有一个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,有不同的对象结构,但都在标记对象下

如果一个人最终得到这样的东西,那将是理想的

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 modal来解析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{$0.type == Type.car.rawValue}
        print("Its car array: \(carObjects)")// if you need filters car object then use this
        let houseObjects = objects.filter{$0.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中,使用枚举而不是结构来实现“灵活类型”。 2.然后需要为“item”和“type”使用解析器。 2.解析,然后切换到解码,并设置正确的类型。 对于上面的JSON,您需要

struct YourFeed: Decodable {
    let stuff: [Item]
}
每件物品都可以是汽车或房子

struct Car: Decodable { ... }
struct House: Decodable { ... }
所以这些都很简单

现在是第二项。它可以是多种类型

在Swift中,使用枚举而不是结构来实现“灵活类型”。 接下来,只需将其镜像到原始枚举中,该枚举将用于解析类型字段。你可以叫它任何东西,我刚刚叫它Parse

    // the relevant key strings for parsing the "type" field:
    private enum Parse: String, Decodable {
        case car
        case house
    }
接下来,查看顶部的原始JSON。每个项目有两个字段,类型和对象。这里是原始枚举。再说一次,你可以叫它任何东西,我刚才在这里叫它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”。 最后,编写项的初始值设定项。只需解码的顶级和类型

    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)
。。。你完成了。使用相关类解码数据,并将Item enum对象设置为适当的类型

解析这些,然后切换到解码/设置枚举。 这是一个完整的过程:

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

此json不是JSONAL json键必须用双引号括起来。这是必需的。它仍然不是有效的JSON。缺少许多逗号和右大括号。发布伪JSON并没有真正的帮助。不,你没有。天哪!!我很抱歉!修正!:对于任何阅读本文的人来说,理解这里的一个关键点是对象不再是结构,它必须是枚举;这是一个很好的解释,谢谢。我不知道如何做到这一点,令人惊讶的是,它没有在更多的地方解释得更清楚!途中赏金
        // 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)
        }
    }
}