Декодирование JSON в Codable Objects - с условием - PullRequest
1 голос
/ 19 июня 2019

Я хотел бы декодировать JSON в объекты с протоколом Codable.

Результат, которого я хочу достичь:

[
 [ Collection
    < collectionType = item
    < collectionName = some name`
    < data = [ Item
                 < itemTitle = title
                 < itemSubtitle = subtitle,
               Item
                 < itemTitle = title
                 < itemSubtitle = subtitle ],
[ Collection
    < collectionType = location
    < collectionName = some name`
    < data = [ Location
                 < locationName = someName,
               Location
                 < locationName = someName ],
[ Collection
    < collectionType = item
    < collectionName = some name`
    < data = [ Item
                 < itemTitle = title
                 < itemSubtitle = subtitle,
               Item
                 < itemTitle = title
                 < itemSubtitle = subtitle ],
[ Collection
    < collectionType = location
    < collectionName = some name`
    < data = [ Location
                 < locationName = someName,
               Location
                 < locationName = someName ]]

JSON выглядит следующим образом:

    [{
        "collectionType": "item",
        "collectionName": "some name",
        "data": [
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            },
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            }
         ]
      },
      {
        "collectionType": "location",
        "collectionName": "some name",
        "data": [
            {
                "locationName": "a name",
            },
            {
                "locationName": "a name",
            }
         ]
      },
      {
        "collectionType": "item",
        "collectionName": "some name",
        "data": [
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            },
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            }
         ]
      },
      {
        "collectionType": "location",
        "collectionName": "some name",
        "data": [
            {
                "locationName": "a name",
            },
            {
                "locationName": "a name",
            }
         ]
      }
  ]

Как вы можете видеть, коллекция будет иметь тип элемента или местоположения.И данные будут в соответствии с этим типом.Как мне добиться этого с Codable?

Мои объекты выглядят следующим образом:

class Collection: NSObject, Codable {

    // MARK: - Properties

    let collectionType: String
    let collectionName: String
    let data????

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case collectionType
        case collectionName
    }
}

class Item: NSObject, Codable {

    // MARK: - Properties

    let itemTitle: String
    let itemSubtitle: String

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case itemTitle
        case itemSubtitle
    }
}

class Location: NSObject, Codable {

    // MARK: - Properties

    let locationName: String

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case locationName
    }
}

Как я могу распространять данные с соответствующими объектами?

Ответы [ 2 ]

1 голос
/ 19 июня 2019

Я предлагаю два подхода:

Подход 1

Измените структуру данных, чтобы устранить неоднозначность того, описывает ли data элемент или местоположение:

[{
    "collectionName": "some name",
    "items": [
        {
            "itemTitle": "title",
            "itemSubtitle": "subtitle",
        },
        {
            "itemTitle": "title",
            "itemSubtitle": "subtitle",
        }
    ]
},
{
    "collectionName": "some name",
    "locations": [
        {
            "locationName": "a name",
        },
        {
            "locationName": "another name",
        }
    ]
}]

... и измените ваш Collection, чтобы иметь необязательный locations и необязательный items.

Подход 2

Если изменение структуры JSON не является вариантом, тогда я предлагаюизменение класса Collection на:

class Collection: Codable {
    let collectionType: String
    let collectionName: String
    let data: [CollectionData]
}

... и создание перечисления CollectionData:

enum CollectionError: Error {
    case invalidData
}

enum CollectionData {
    case item(Item)
    case location(Location)
}

extension CollectionData: Codable {
    init(from decoder: Decoder) throws {
        if let item = try? Item(from: decoder) {
            self = .item(item)
            return
        }

        if let location = try? Location(from: decoder) {
            self = .location(location)
            return
        }

        throw CollectionError.invalidData
    }

    func encode(to encoder: Encoder) throws {
        switch self {
        case .item(let item):
            try item.encode(to: encoder)
        case .location(let location):
            try location.encode(to: encoder)
        }
    }
}

Плюсы и минусы двух подходов:

Подход 1

Pro: делает данные более информативными

Con: Позволяет коллекции не использовать ни items, ни locations

Подход 2

Pro: работает с существующей структурой данных

Con: Позволяет использовать массив data, который был бы частично Location и частично Item

Если в вашем реальном коде нет ничего, выпохоже, что CodingKeys определяется точно так, как было бы по умолчанию, поэтому вы, вероятно, можете удалить его.

0 голосов
/ 19 июня 2019

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

Например, если itemTitle равно nil, выполните логику locationName и т. Д.

class Collection: NSObject, Codable {
    let collectionType: String
    let collectionName: String
    let data:data?

    private enum CodingKeys: String, CodingKey {
        case collectionType
        case collectionName
    }
}

class data: NSObject, Codable {

    let itemTitle: String?
    let itemSubtitle: String?
    let locationName: String?

    private enum CodingKeys: String, CodingKey {
        case itemTitle
        case itemSubtitle
        case locationName
    }
}
...