简体   繁体   中英

Swift 4 Decodable: Decoding complex JSON

I have to decode an array of the dictionary, where the key is an enum & value is a model object.

Here is my sample 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
    },
  }
]

Here is my decoding code,

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

Here GIFFormat is Enum & Media is the model object, and they are decoding perfectly fine.

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?
}

My console prints,

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

Can anyone explain to me what is exactly wrong here?

Even though the rawValue for GIFFormat is String , GIFFormat itself is an enum. You should update

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

to

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

UPDATE: In response to your comment

Now to access value, I need to use like this, collection?.first?[GIFFormat.mp4.rawValue]?.url . Which is again ugly !!

I would suggest a bit of refactoring. You can start by removing your enum altogether. Keep your Media struct. Create a new Collection struct

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

Then, you can update the above line to

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

and your ugly line transforms into

collection.first?.nanomp4.url

NOTE: This solution assumes that you only have nanomp4 & nanowebm as your enum values. If this is not the case, then this might not be the best solution and you might have to go with the first solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM