skip to Main Content

I am using below code to parse json from API


struct ResourceInfo: Decodable {
    let id: String
    let type: String
    // let department: String. -> Unable to get the value for department
}

struct CustomerInfo: Decodable {
    let name: String
    let country: String
    let resources: [ResourceInfo]

    enum CodingKeys: CodingKey {
        case name
        case country
        case resources
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.country = try container.decode(String.self, forKey: .country)

        let resourcesDict = try container.decode([String: ResourceInfo].self, forKey: .resources)
        //print(resourcesDict.map { $0.key })
        self.resources = resourcesDict.map { $0.value }
    }
}

static func parseJson() {
        let json = """
        {
          "name": "John",
          "country": "USA",
          "resources": {
            "electronics": {
              "id": "101",
              "type": "PC"
            },
            "mechanical": {
              "id": "201",
              "type": "CAR"
            },
            "science": {
              "id": "301",
              "type": "CHEM"
            }
          }
        }
        """
        let result = try? JSONDecoder().decode(CustomerInfo.self, from: json.data(using: .utf8)!)
        dump(result)
    }

Output:

Optional(JsonSample.CustomerInfo(name: "John", country: "USA", resources: [JsonSample.ResourceInfo(id: "201", type: "CAR"), JsonSample.ResourceInfo(id: "301", type: "CHEM"), JsonSample.ResourceInfo(id: "101", type: "PC")]))
  ▿ some: JsonSample.CustomerInfo
    - name: "John"
    - country: "USA"
    ▿ resources: 3 elements
      ▿ JsonSample.ResourceInfo
        - id: "201"
        - type: "CAR"
      ▿ JsonSample.ResourceInfo
        - id: "301"
        - type: "CHEM"
      ▿ JsonSample.ResourceInfo
        - id: "101"
        - type: "PC"

Could someone help me get the value department like electronics, mechanical & science? Thank you

3

Answers


  1. You throw away the information in the line resourcesDict.map { $0.value } because you ignore the keys.

    My suggestion is to add a temporary helper struct, decode that and map the dictionary to ResourceInfo with both key and value

    struct Temp : Decodable {
        let id, type: String
    }
    
    struct ResourceInfo {
        let id: String
        let type: String
        let department: String
    }
    
    struct CustomerInfo: Decodable {
        let name: String
        let country: String
        let resources: [ResourceInfo]
    
        enum CodingKeys: CodingKey { case name, country, resources }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.name = try container.decode(String.self, forKey: .name)
            self.country = try container.decode(String.self, forKey: .country)
    
            let resourcesDict = try container.decode([String: Temp].self, forKey: .resources)
            self.resources = resourcesDict.map {
                ResourceInfo(id: $1.id, type: $1.type, department: $0)
            }
        }
    }
    
    Login or Signup to reply.
  2. You could try this approach, where you decode ResourceInfo initially with just its CodingKeys,
    then remap the results with the dictionary keys as the department,
    as shown in the SwiftUI example code:

    struct ContentView: View {
        @State var customerInfo: CustomerInfo?
        
        var body: some View {
            VStack {
                if let customer = customerInfo {
                    Text(customer.name)
                    List(customer.resources) { resourse in
                        Text(resourse.department)
                    }
                }
            }
            .onAppear {
                parseJson()
            }
        }
        
        func parseJson() {
            let json = """
                {
                  "name": "John",
                  "country": "USA",
                  "resources": {
                    "electronics": {
                      "id": "101",
                      "type": "PC"
                    },
                    "mechanical": {
                      "id": "201",
                      "type": "CAR"
                    },
                    "science": {
                      "id": "301",
                      "type": "CHEM"
                    }
                  }
                }
                """
    
            do {
                let result = try JSONDecoder().decode(CustomerInfo.self, from: json.data(using: .utf8)!)
                print("n---> result: (result)n")
                customerInfo = result
            } catch {
                print(error)
            }
        }
        
    }
    
    struct ResourceInfo: Identifiable, Decodable { // <-- here
        let id: String
        let type: String
        var department: String = "" // <-- here
        
        enum CodingKeys: CodingKey {  // <-- here
            case id
            case type
        }
    }
    
    struct CustomerInfo: Decodable {
        let name: String
        let country: String
        let resources: [ResourceInfo]
    
        enum CodingKeys: CodingKey {
            case name
            case country
            case resources
        }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.name = try container.decode(String.self, forKey: .name)
            self.country = try container.decode(String.self, forKey: .country)
    
            let resourcesDict = try container.decode([String: ResourceInfo].self, forKey: .resources)
    
            // -- here
            resources = resourcesDict.map { ResourceInfo(id: $0.value.id, type: $0.value.type, department: $0.key) }
        }
    }
    
    Login or Signup to reply.
  3. The difficulty lies in the fact that the data structure you want doesn’t map directly with the hierarchy in the JSON. The department name is a key to a dictionary containing the id and type values. If you want to collapse that information into a single struct then this is one solution. There may be smarter ways to do this. I just added a separate structure for decoding the id and type:

    
    struct ResourceInfoDetails: Decodable {
        let id: String
        let type: String
    }
    
    struct ResourceInfo: Decodable {
        let department: String
        let id: String
        let type: String
    }
    
    struct CustomerInfo: Decodable {
        let name: String
        let country: String
        let resources: [ResourceInfo]
    
        enum CodingKeys: CodingKey {
            case name
            case country
            case resources
        }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.name = try container.decode(String.self, forKey: .name)
            self.country = try container.decode(String.self, forKey: .country)
    
            let resourcesDict = try container.decode([String: ResourceInfoDetails].self, forKey: .resources)
            self.resources = resourcesDict.map { ResourceInfo(department: $0, id: $1.id, type: $1.type) }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search