2

Given a JSON object with nested object structure that looks like this:

{
   "users":[
      {
         "user":{
            "name":"Adam",
            "age":25
         },
         "address":{
            "city":"Stockholm",
            "country":"Sweden"
         }
      },
      {
         "user":{
            "name":"Lilly",
            "age":24
         },
         "address":{
            "city":"Copenhagen",
            "country":"Denmark"
         }
      }
   ]
}

How can one implement correct Decodable implementation for an object that looks like this.

struct User {
  struct Address {
    let city: String
    let country: String
  }

  let name: String
  let age: Int
  let address: Address
}

Notice that the Swift struct contains a nested struct Address, while the JSON object has address in a separate object. Is it possible to create the Decodable implementation that handles this scenario?

I've tried various approaches, but all of them included creation of intermediary objects that would later map to the User struct. Is it possible to create an implementation that doesn't involve creation of these intermediary objects?

Said Sikira
  • 4,482
  • 29
  • 42
  • 1
    Does this answer your question? [How to decode a nested JSON struct with Swift Decodable protocol?](https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol) – pawello2222 Jun 16 '20 at 08:01

2 Answers2

4

You can use a custom decoder so that you don't have to create the other objects.

struct User: Decodable {
    let name: String
    let age: Int
    let address: Address


    struct Address: Decodable {
        let city: String
        let country: String
    }

    enum CodingKeys: String, CodingKey {
        case user
        case address
    }

    enum UserKeys: String, CodingKey {
        case name
        case age
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let user = try container.nestedContainer(keyedBy: UserKeys.self, forKey: .user)
        name = try user.decode(String.self, forKey: .name)
        age = try user.decode(Int.self, forKey: .age)
        address = try container.decode(Address.self, forKey: .address)

    }
}

So putting your data into a playground

let data = """
{
   "users":[
      {
         "user":{
            "name":"Adam",
            "age":25
         },
         "address":{
            "city":"Stockholm",
            "country":"Sweden"
         }
      },
      {
         "user":{
            "name":"Lilly",
            "age":24
         },
         "address":{
            "city":"Copenhagen",
            "country":"Denmark"
         }
      }
   ]
}
""".data(using: .utf8)!

You can decode like this:

let decoder = JSONDecoder()
let result = try! decoder.decode([String:[User]].self, from: data)

Or you can create a Users struct so that you don't have to deal with dictionaries

struct Users: Decodable {
    let users: [User]
}

let decoder = JSONDecoder()
let result = try! decoder.decode(Users.self, from: data)
Andrew
  • 26,706
  • 9
  • 85
  • 101
1

Did you mean to flatten the struct using computed properties like this:

struct User: Codable {
    struct UserDetail: Codable {
        let name: String
        let age: Int
    }
    struct Address: Codable {
        let city: String
        let country: String
    }

    let user: UserDetail
    let address: Address

    var name: String { user.name}
    var age: Int { user.age }
    var city: String { address.city }
    var country: String { address.country }
}
print(user.name, user.age, user.city, user.country) // all the properties accessible within User struct
Frankenstein
  • 15,732
  • 4
  • 22
  • 47
  • Not really, the problem I'd like to solve is how to actually decode this JSON object into the Swift struct that has another nested struct inside it. – Said Sikira Jun 16 '20 at 07:52
  • You could use a custom decoder init method for that. You could use @Andrew's method or flatten everything using computed property to base struct like I did. – Frankenstein Jun 16 '20 at 07:55
  • Upvoting because this solved my specific problem, if not OPs – snarik Jan 21 '21 at 19:24