1

Below is my json response and the struct which I need to costruct out of it.

condition: I would not like to create any other struct apart from Response,Media and would like to parse in single line as specified below.

{
     "name": "xxxx",
     "title": "xxxxxxx",
     "assets": [
        {
          "items": [
            {
              "id": "eeee",
              "desc": "rrrrrr"
            },            {
              "id": "eeee",
            },            {
              "desc": "rrrrrr"
            }]
        }]
}



struct Response {
    let name  : String
    let title : string
    let items : [Media]

    private enum codingKeys : String, CodingKey {
        case name = "name"
        case title = "title"
        case items = "assets.items"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: codingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        title = try container.decode(TrayLayout.self, forKey: .title)
        items = try container.decode([Media].self, forKey: .items)
    }
}
1
  • please take a look at following answer and see if that helps to achieve your goal. Commented Jan 30, 2020 at 13:50

3 Answers 3

2

I managed to find a solution for your problem.

Considering following is sample json

let jsonString = """
{
     "name": "xxxx",
     "title": "xxxxxxx",
     "assets": [
        {
          "items": [
            {
              "id": "id11",
              "desc": "desc11"
            },            {
              "id": "id12",
            },            {
              "desc": "desc13"
            }]
        },{
          "items": [
            {
              "id": "id21",
              "desc": "desc21"
            },            {
              "id": "id22",
            },            {
              "desc": "desc23"
            }]
        }]
}
"""

Your structures will look as below

struct Media: Codable {
    let id,desc: String?
    enum CodingKeys: String, CodingKey {
        case id,desc
    }
}

struct Response: Decodable {
    let name,title: String?
    let items: [Media]?
    enum CodingKeys: String, CodingKey {
        case name,title,items
        case assets = "assets"
    }
    // Decoding
    init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        title = try container.decode(String.self, forKey: .title)

        // Here as the assets contains array of object which has a key as items and then again consist of a array of Media we need to decode as below
        let assets = try container.decode([[String:[Media]]].self, forKey: .assets)

        // At this stage will get assets with a array of array so we first map and retrive the items and then reduce them to one single array
        items = assets.compactMap{$0[CodingKeys.items.rawValue]}.reduce([], +)
    }
}

At the end it's time to use it as follows

let data = jsonString.data(using: .utf8)!
let myResponse = try! JSONDecoder().decode(Response.self, from: data)

Now you can access the data as below

myResponse.name
myResponse.title
myResponse.items

Hope this basic code helps you to achieve what you want to do. And then you can go ahead and do more nested parsing(s).

I would like to thanks Nic Laughter for such a detailed article; by referring which I managed to come with above solution.

Sign up to request clarification or add additional context in comments.

Comments

1

Found a new way:

struct Media: Codable {
    let id,desc: String?
    enum CodingKeys: String, CodingKey {
        case id,desc
    }
}
struct Response: Decodable, CustomStringConvertible {
    let name,title: String?
    @NestedKey
    let items: [Media]?
    enum CodingKeys: String,NestableCodingKey {
        case name,title,
        case items = "assets/items"
    }

}

Comments

0

You can try this:

struct Response: Decodable {
  let name, title: String
  let assets: [Assets]
}

struct Assets: Decodable {
 let items: [Items]
}

struct Items: Decodable {
  let id, desc: String
}

Then you can just decode it like this:

guard let response = try? JSONDecoder().decode(Response.self, from: data) else { print("Response not parsed"); return }

1 Comment

Hey rob, i would not like to create new assets struct

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.