Декодируется для JSON с двумя структурами под одним тегом - PullRequest
0 голосов
/ 02 октября 2019

У меня есть этот 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 }}
]}

Как вы можете видеть для " машина " и " дом " есть разные «объект» структурирован, но оба под тэгом «объект».

Было бы идеально, если бы кто-то закончил с чем-то вроде

struct StuffItem: Decodable {       
  let type: TheType
  let car: Car
  let house: House
}

Есть ли какой-нибудь Codable, быстрый способ обработкиэто?

Ответы [ 3 ]

5 голосов
/ 02 октября 2019

Способ самый быстрый , на мой взгляд, - это перечисление со связанными типами

Это является действительным 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)
}
1 голос
/ 03 октября 2019

Вы можете обрабатывать несколько случаев, используя enum, просто определив свой тип. Предоставление кода поможет вам разобрать JSON, используя модальную структуру Struct с enum.

// 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)
    }
}
0 голосов
/ 29 октября 2019

Еще одно объяснение:

Поскольку здесь не так много объяснений, вот еще один пример того, что объяснил @vadian:

1. В Swift использует enum вместо struct для достижения «гибкого типа».

2. Затем вам понадобятся парсеры для «элемента» и для «типа».

2. Разбор, а затем просто переключитесь на декодирование и установите правильный тип.

Так что для 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)

Далее просто отразите это внеобработанное перечисление, которое будет использоваться для анализа поля типа. (Вы можете назвать это как угодно, я только что назвал это "Parse".)

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

Далее, посмотрите на исходный JSON сверху. Каждый «элемент» имеет два поля: «тип» и «объект». Вот они в сыром перечислении. (Опять же, вы можете называть это как угодно, я только что назвал это «Ключами».)

    // 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". Просто декодируйте верхний уровень и «тип» ...

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