可解码为JSON,在同一标记下有两个结构体

qybjjes1  于 2023-04-22  发布在  其他
关注(0)|答案(3)|浏览(131)

我有这个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
}

有没有一种可编程的,快速的方法来处理这个问题?

h79rfbju

h79rfbju1#

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

This是有效的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)
}
s4n0splo

s4n0splo2#

你可以通过使用枚举来处理多种情况,只需定义你的类型,给出代码将帮助你通过使用带有枚举的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{$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)
    }
}
7kqas0il

7kqas0il3#

另一个解释:

由于没有太多的解释,这里是@vadian解释的另一个例子:

1. Swift中 * 使用enum而不是struct* 来实现“灵活类型”。

2.然后你需要对'item'和'type'进行解析。

3.解析,然后切换到解码,设置正确的类型即可。

所以对于上面的JSON,你需要

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

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

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

所以这些都很容易。
现在是Item。它可以是多个类型。

在Swift中,* 使用enum* 而不是struct来实现“灵活类型”。

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

有一个enum来解析'item'级别,还有一个enum来解析'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)

解码数据(使用相关的类),并将“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)
        }
    }
}

相关问题