-1

Suppose I have an API that returns this json:

{
   "dogs": [{"name": "Bella"}, {"name": "Lucy"}], 
   "cats": [{"name": "Oscar"}, {"name": "Coco"}]
}

And a model that looks like this:

import Foundation

public struct Animal: Codable {
    let name: String?
}

Now I want to decode the array of Animal from the "dogs" key:

let animals = try JSONDecoder().decode([Animal].self, from: response.data!)

However, I somehow have to reference the "dogs" key. How do I do this?

etayluz
  • 15,920
  • 23
  • 106
  • 151

4 Answers4

2

First of all, the JSON you provided is not valid JSON. So let's assume that what you actually mean is this:

{
    "dogs": [{"name": "Bella"}, {"name": "Lucy"}],
    "cats": [{"name": "Oscar"}, {"name": "Coco"}]
}

Then the problem with your code is merely this line:

let animals = try JSONDecoder().decode([Animal].self, from: response.data!)

You're claiming that the JSON represents an array of Animal. But it doesn't. It represents a dictionary with keys dogs and cats. So you just say so.

struct Animal: Codable {
    let name: String
}
struct Animals: Codable {
    let dogs: [Animal]
    let cats: [Animal]
}

Now everything will just work:

let animals = try JSONDecoder().decode(Animals.self, from: response.data!)
matt
  • 515,959
  • 87
  • 875
  • 1,141
0

You can all get all values from JSON like this:

let arrayOfResponse = Array(response.data.values)
let clinicalTrial = try JSONDecoder().decode([Animal].self, from: arrayOfResponse!)
etayluz
  • 15,920
  • 23
  • 106
  • 151
Jogendar Choudhary
  • 3,476
  • 1
  • 12
  • 26
0

if you know keys previously like dogs, cats you can do like this

   struct Initial: Codable {
        let dogs, cats: [Animal]
    }

    struct Animal: Codable {
        let name: String
    }

    // MARK: Convenience initializers

    extension Initial {
        init(data: Data) throws {
            self = try JSONDecoder().decode(Initial.self, from: data)
        }

        init(_ json: String, using encoding: String.Encoding = .utf8) throws {
            guard let data = json.data(using: encoding) else {
                throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
            }
            try self.init(data: data)
        }
    }


// data  is your response data

  if let inital = try? Initial.init(data: data) {
                let cats = inital.cats
                let dogs = inital.dogs
            }
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35
0

Your JSON is slightly off, you will have to put double quotes around your name, but that way you can run the following Playground:

import Cocoa

let jsonData = """
{
   "dogs": [{"name": "Bella"}, {"name": "Lucy"}],
   "cats": [{"name": "Oscar"}, {"name": "Coco"}]
}
""".data(using: .utf8)!

public struct Animal: Codable {
    let name: String
}

do {
    let anims = try JSONDecoder().decode([String:[Animal]].self, from:jsonData)
    print(anims)
    for kind in anims.keys {
        print(kind)
        if let examples = anims[kind] {
            print(examples.map {exa in exa.name })
        }
    }
} catch {
    print(error)
}

This will not restrict you to cats and dogs, but it is usually a bad idea to use "unknown" keys as data elements in a hash. If you can modify your JSON (which you should since it is not very well structured anyways) you could also move the "kind" of animals to some data element in an array of hashes which will be much more flexible.

Patru
  • 4,481
  • 2
  • 32
  • 42