Как декодировать именованный массив объектов json в Swift

У меня есть именованный массив объектов json, которые я получаю через вызов API.

{
    "Images": [{
        "Width": 800,
        "Height": 590,
        "Url": "https://obfuscated.image.url/image1.jpg"
        }, {
        "Width": 800,
        "Height": 533,
        "Url": "https://obfuscated.image.url/image2.jpg"
        }, {
        "Width": 800,
        "Height": 478,
        "Url": "https://obfuscated.image.url/image3.jpg"
    }]
}

Объекты имеют тип изображения, который я определил, и у них есть функция декодирования, которая может декодировать один объект изображения. Изображение выглядит так:

struct Image : Codable {
    let width: CGFloat
    let height: CGFloat
    let url: String

    enum ImageKey: String, CodingKey {
        case width = "Width"
        case height = "Height"
        case url = "Url"
    }

    init(from decoder: Decoder) throws
    {
        let container = try decoder.container(keyedBy: ImageKey.self)
        width = try container.decodeIfPresent(CGFloat.self, forKey: .width) ?? 0.0
        height = try container.decodeIfPresent(CGFloat.self, forKey: .height) ?? 0.0
        url = try container.decodeIfPresent(String.self, forKey: .url) ?? ""
    }

    func encode(to encoder: Encoder) throws
    {
    }
}

Я написал тест для этого сценария, но здесь я в тупике! Тест не проходит (естественно) и выглядит так:

func testManyImages() throws {

    if let urlManyImages = urlManyImages {
        self.data = try? Data(contentsOf: urlManyImages)
    }

    let jsonDecoder = JSONDecoder()
    if let data = self.data {
        if let _images:[Image] = try? jsonDecoder.decode([Image].self, from: data) {
            self.images = _images
        }
    }

    XCTAssertNotNil(self.images)
}

Мой вопрос таков:

Как получить доступ к массиву изображений по имени "Изображения" или как-то иначе?

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


person Damo    schedule 08.06.2018    source источник


Ответы (2)


Я думаю, что ваша структура Codable неверна. Так должно быть :

struct JSONStructure: Codable {
    let images: [Images]
    private enum CodingKeys: String, CodingKey {
        case images = "Images"
    }
}

struct Images: Codable {
    let width: CGFloat
    let height: CGFloat
    let url: String

    private enum CodingKeys: String, CodingKey {
        case width  = "Width"
        case height = "Height"
        case url    = "Url"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        width         = try container.decodeIfPresent(CGFloat.self, forKey: .width) ?? 0.0
        height        = try container.decodeIfPresent(CGFloat.self, forKey: .height) ?? 0.0
        url           = try container.decodeIfPresent(String.self, forKey: .url) ?? ""
    }

    func encode(to encoder: Encoder) throws {
    }
}

А потом :

if let data = self.data {
   if let decoded = try? jsonDecoder.decode(JSONStructure.self, from: data) {
       print("Decoded : \(decoded)")
       self.images = decoded.images
   }
}

Журналы:

Расшифровано: JSONStructure(изображения: [DreamSiteradio.Images(ширина: 800,0, высота: 590,0, URL: "https://obfuscated.image.url/image1.jpg"), DreamSiteradio.Images(ширина: 800,0, высота: 533,0, URL: "https://obfuscated.image.url/image2.jpg"), DreamSiteradio.Images(ширина: 800,0, высота: 478,0, URL: "https://obfuscated.image.url/image3.jpg")])

person Sharad Chauhan    schedule 08.06.2018
comment
Спасибо @Шарад Чаухан - person Damo; 08.06.2018

Вы можете создать 2 структуры вместо 1 для анализа полного ответа, т.е.

struct Response: Codable
{
    let images: [Image]?
    enum CodingKeys: String, CodingKey
    {
        case images = "Images"
    }
}
struct Image : Codable
{
    var width: CGFloat?
    let height: CGFloat?
    let url: String?

    enum CodingKeys: String, CodingKey
    {
        case width = "Width"
        case height = "Height"
        case url = "Url"
    }
}

Поскольку в ответе есть вложенные уровни, я создал несколько структур.

Также вам не нужно писать явный контейнер для синтаксического анализа. Codable сделает это самостоятельно.

Вы можете просто декодировать образец JSON с помощью:

if let data = jsonStr.data(using: .utf8)
{
    let response = try? JSONDecoder().decode(Response.self, from: data)
    print(response?.images)
}
person PGDev    schedule 08.06.2018