'Custom Decodable JSON - Get first array element

I have JSON data that looks like this:

{
  // Other attributes here....
  "id": "a4s5d6f8ddw",
  "images": {
    "selection": [
      {
        "url": "https://www.myimage.com"
      },
      // ... more images here
    ]
  }
}

I want to extract the id and the first url from the nested JSON and store it in my struct. I've tried something like this, but I can't quite get it:

struct MyImage {
  let id: String
  let url: URL

  enum CodingKeys: CodingKey {
    case id
    case images
  }

  enum Selection: CodingKey {
    case selection
  }

  enum ImageURL: CodingKey {
     case url
  }
}

extension MyImage: Decodable {
  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    id = try values.decode(String.self, forKey: .id)

    let selection = try values.nestedContainer(keyedBy: Selection.self, forKey: .selection)
    // Then what?? Need to extract just the first one and then decode to a URL
}

How can I finish this off?



Solution 1:[1]

You're on the right track, but some minor errors:

struct MyImage {
    let id: String
    let url: URL

    enum CodingKeys: CodingKey {
        case id
        case images
    }

    // Keys inside of Images
    enum ImagesKeys: CodingKey {
        case selection
    }

    // Struct inside of Selection
    private struct Selection: Decodable {
        let url: URL
    }
}

With that, the decoder is close to what you were thinking:

extension MyImage: Decodable {
    init(from decoder: Decoder) throws {
        // Decode the outer stuff
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(String.self, forKey: .id)

        // Pull off the images object
        let images = try values.nestedContainer(keyedBy: ImagesKeys.self, forKey: .images)

        // Extract the selections array
        var selections = try images.nestedUnkeyedContainer(forKey: .selection)

        // Take the first element
        let firstSelection = try selections.decode(Selection.self)

        // Done.
        self.url = firstSelection.url
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rob Napier