Swift 4 Decodable: декодирующий комплекс JSON - PullRequest
0 голосов
/ 29 августа 2018

Мне нужно декодировать массив словаря, где ключ - это enum, а значение - это объект модели.

Вот мой пример JSON,

[
  {
    "nanomp4": {
      "url": "https://media.tenor.com/videos/a1da4dcf693c2187615721d866decf00/mp4", 
      "dims": [
        150, 
        138
      ], 
      "duration": 2.0, 
      "preview": "https://media.tenor.com/images/17d523e6b7c3c9a4ca64566a1890d94d/tenor.png", 
      "size": 70381
    }, 
    "nanowebm": {
      "url": "https://media.tenor.com/videos/aa983425114e32ab446f669d91611938/webm", 
      "dims": [
        150, 
        138
      ], 
      "preview": "https://media.tenor.com/images/17d523e6b7c3c9a4ca64566a1890d94d/tenor.png", 
      "size": 53888
    }, 
  },
  {
    "nanomp4": {
    "url": "https://media.tenor.com/videos/a1da4dcf693c2187615721d866decf00/mp4",
    "dims": [
          150,
          138
          ],
    "duration": 2.0,
    "preview": "https://media.tenor.com/images/17d523e6b7c3c9a4ca64566a1890d94d/tenor.png",
    "size": 70381
    },
  }
]

Вот мой код декодирования,

do {
    let data = try Data(contentsOf: fileURL)
    let decoder = JSONDecoder()
    let collection = try decoder.decode([[GIFFormat:Media]].self, from: data)

    print(collection)
} catch {
    print("Error in parsing/decoding JSON: \(error)")
}

Здесь GIFFormat - это Enum & Media - объект модели, и они прекрасно декодируют.

enum GIFFormat: String, Decodable {

    case nanoMP4    = "nanomp4"
    case nanoWebM   = "nanowebm"
}

struct Media: Decodable {
    let url: URL?        
    let dims: [Int]?
    let duration: Double?
    let preview: URL?
    let size: Int64?
}

Отпечатки моей консоли,

typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

Может кто-нибудь объяснить мне, что именно здесь не так?

1 Ответ

0 голосов
/ 29 августа 2018

Даже если rawValue для GIFFormat равно String, GIFFormat само по себе является перечислением. Вы должны обновить

let collection = try decoder.decode([[GIFFormat:Media]].self, from: data)

до

let collection = try decoder.decode([[GIFFormat.RawValue:Media]].self, from: data)

ОБНОВЛЕНИЕ: В ответ на ваш комментарий

Теперь для доступа к значению мне нужно использовать вот так, collection?.first?[GIFFormat.mp4.rawValue]?.url. Что опять-таки безобразно !!

Я бы предложил немного рефакторинга. Вы можете начать с удаления вашего enum в целом. Сохраняйте свою Media структуру. Создайте новую Collection struct

struct Collection: Decodable {
    let nanomp4: Media!
    let nanowebm: Media!
}

Затем вы можете обновить вышеприведенную строку до

let collection = try decoder.decode([Collection].self, from: data)

и ваша безобразная строка преобразуется в

collection.first?.nanomp4.url

ПРИМЕЧАНИЕ: В этом решении предполагается, что в качестве значений перечисления у вас есть только nanomp4 & nanowebm. Если это не так, то это может быть не лучшее решение, и вам, возможно, придется использовать первое решение.

...