Декодировать данные, которые могут меняться время от времени - PullRequest
1 голос
/ 13 октября 2019

У меня есть структура с именем Info, которая декодируется на основе полученных данных. Но иногда одно из значений в данных может быть либо double, либо массивом double. Как мне настроить мою структуру для этого?

Вот моя структура:

struct Info: Decodable {

    let author: String
    let title: String
    let tags: [Tags]
    let price: [Double]

    enum Tags: String, Decodable {
        case nonfiction
        case biography
        case fiction
    }

}

Исходя из URL, я получаю цену как двойную

{
    "author" : "Mark A",
    "title" : "The Great Deman",
    "tags" : [
      "nonfiction",
      "biography"
    ],
    "price" : "242"

}

или я получаю его как массив значений типа double

{
    "author" : "Mark A",
    "title" : "The Great Deman",
    "tags" : [
      "nonfiction",
      "biography"
    ],
    "price" : [
    "242",
    "299",
    "335"
    ]

}

Я хочу настроить свою структуру так, чтобы, если я получаю значение типа double вместо массива значений типа double, цена была декодирована как массив из 1 double.

Любая помощь будет высоко оценена.

Спасибо

1 Ответ

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

Ваш JSON на самом деле является либо строкой, либо массивом строк. Поэтому вам нужно создать собственный декодер для декодирования, а затем преобразовать их в Double:

struct Info {
    let author, title: String
    let tags: [Tags]
    let price: [Double]
    enum Tags: String, Codable {
        case nonfiction, biography, fiction
    }
}

extension Info: Codable {
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        author = try container.decode(String.self, forKey: .author)
        title  = try container.decode(String.self, forKey: .title)
        tags = try container.decode([Tags].self, forKey: .tags)
        do {
            price = try [Double(container.decode(String.self, forKey: .price)) ?? .zero]
        } catch {
            price = try container.decode([String].self, forKey: .price).compactMap(Double.init)
        }
    }
}

Тестирование игровой площадки

let infoData = Data("""
{
    "author" : "Mark A",
    "title" : "The Great Deman",
    "tags" : [
      "nonfiction",
      "biography"
    ],
    "price" : "242"

}
""".utf8)
do {
    let info = try JSONDecoder().decode(Info.self, from: infoData)
    print("price",info.price)  // "price [242.0]\n"
} catch {
    print(error)
}

let infoData2 = Data("""
{
    "author" : "Mark A",
    "title" : "The Great Deman",
    "tags" : [
      "nonfiction",
      "biography"
    ],
    "price" : [
    "242",
    "299",
    "335"
    ]

}
""".utf8)

do {
    let info = try JSONDecoder().decode(Info.self, from: infoData2)
    print("price",info.price)  // "price [242.0, 299.0, 335.0]\n"
} catch {
    print(error)
}
...