0

I want to decode this data and want to display fields separately in UI. Json data I am receiving from API

{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [
    {
        "PackageId": 1025,
        "PackageName": "17 OH Progesterone",
        "Price": 0.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "50",
                "SampleName": "Serum",
                "ColourCode": "#FFB500"
            }
        ]
    },
    {
        "PackageId": 1916,
        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
        "Price": 120.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 1914,
        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
        "Price": 110.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 1913,
        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
        "Price": 12.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 936,
        "PackageName": "24 Hours Urinary Phosphorous",
        "Price": 15.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 937,
        "PackageName": "24 Hours Urinary Potassium",
        "Price": 2.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
   ......
   ]}

Decoding Model for the above

import Foundation


          struct PriceList {
                           let Success: Bool
                           let message: String
                           let Response: [ResponseList]?
                           }

         extension PriceList:Codable
                    {
                   enum CodingKeys: String, CodingKey 
                          {
                          case Success = "IsSuccess"
                          case message = "Message"
                          case Response = "ResponseData"
                         }

        init(from decoder:Decoder) throws {
  
                  let container = try decoder.container(keyedBy:CodingKeys.self)
                  Success = try container.decode(Bool.self,forKey: .Success)    
                  message = try container.decode(String.self,forKey: .message)
                  Response = try container.decode([ResponseList].self,forKey: .Response)
                          }
                       }


                struct ResponseList 
                         {
                             let packageID: Int
                             let packageName: String
                             let price, discountedPrice: Double
                             let type: String
                             let testPackageGroupID: Int
                             let SampleType: [SampleTypeList]?
                         }
               extension ResponseList:Decodable
                         {
                      enum CodingKeys: String, CodingKey {
                             case packageID = "PackageId"
                             case packageName = "PackageName"
                             case price = "Price"
                             case discountedPrice = "DiscountedPrice"
                             case type = "Type"
                             case testPackageGroupID = "TestPackageGroupId"
                             case SampleType= "SampleTypeList"
                         }

              init(from decoder:Decoder) throws
                             {
  
                       let container = try decoder.container(keyedBy:CodingKeys.self)
                       packageID = try container.decode(String.self,forKey: .packageID)    
                       packageName= try container.decode(String.self,forKey: .packageName)
                       price= try container.decode(Double.self,forKey: .price)
                  discountedPrice= try container.decode(Double.self,forKey:.discountedPrice)
                     type= try container.decode(String.self,forKey: .type)
          testPackageGroupID = try container.decode(String.self,forKey:  .testPackageGroupID )
             SampleType= try container.decode([SampleTypeList].self,forKey: .SampleType) 
                                   }
                        }


               struct SampleTypeList        
                                 {
                          let testSampleTypeID, sampleName, colourCode: String
                                 }
               extension SampleTypeList:Codable {
                          enum SampleKeys: String, CodingKey {
                          case testSampleTypeID = "TestSampleTypeId"
                          case sampleName = "SampleName"
                          case colourCode = "ColourCode"
                                 }
               init(from decoder:Decoder) throws 
                         {
                          let container = try decoder.container(keyedBy:SampleKeys.self)
          testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )    
                      sampleName = try container.decode(String.self,forKey: .sampleName )
                      colourCode = try container.decode(String.self,forKey: .colourCode)
                         }
                       }

This is the code written in playground:

 var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "xx.xx.xxx.x"
urlComponents.path = "/api/test/home"
urlComponents.queryItems = [URLQueryItem(name: "pricelistGroupId",value: "12")]

let url = urlComponents.url

var request = URLRequest(url: url!)


request.addValue("application/json", forHTTPHeaderField: 
"Accept")
request.addValue("Basic \(authToken)", forHTTPHeaderField: 
 "Authorization")


let task = URLSession.shared.dataTask(with: request)
{
    (data, response, error) in
   
    if let error = error 
    {
    print("Error \(error)")
    return
    }
    if let response = response as? HTTPURLResponse {
    print("Response \(response.statusCode)")
   
    }
   if let data = data
 {

  let dataString = String(data:data, encoding: .utf8)
  print(dataString)
  let json = try? JSONDecoder().decode(PriceList.self,from:data)
 print(json)
}
}

print(dataString) is printing the data. However, no data for print(json) it is showing nil in playground.

ResponseList init is stuck at 259 times (right side playground tab showing all process) whereas SampleTypeList is stuck at 346 times.

If I remove ? (optional) from [ResponseList]? and [SampleTypeList]? it showing "Cannot get unkeyed decoding container -- found null value instead."

Please, ignore typo errors.

Program is stuck at where it is finding null for eg two instances mainly

SampleTypeList = null (occurs many times in JSON) testPackageGroupID = null

4
  • SampleTypeList = null (occurs many times in JSON) testPackageGroupID = null, please share a valid JSON where you have all the cases: On SampleTypeList not null, one null, one with testPackageGroupID null, one with both if there is, that's the real input sample that matters. Commented Apr 8, 2022 at 7:27
  • totally agree with @Larme, to investigate why you are getting the null errors, show us exactly what you get from your print(dataString) all of it, not just a portion. Commented Apr 8, 2022 at 7:38
  • instance of sampletypelist = null ......{ "PackageId": 1926, "PackageName": "Dust Panel", "Price": 3410.0, "DiscountedPrice": 1.0, "Type": "Test", "TestPackageGroupId": 3, "SampleTypeList": null }, Commented Apr 8, 2022 at 12:28
  • instance of TestPackageGroupId...... "PackageId": 1939, "PackageName": "Gliadin IgA Antibodies", "Price": 10.0, "DiscountedPrice": 1.0, "Type": "Test", "TestPackageGroupId": null, "SampleTypeList": [ { "TestSampleTypeId": "50", "SampleName": "Serum", "ColourCode": "#FFB500" } Commented Apr 8, 2022 at 12:30

2 Answers 2

1

This code is enough:

struct PriceList: Decodable {
    let success: Bool
    let message: String
    let response: [ResponseList]

    enum CodingKeys: String, CodingKey {
        case success = "IsSuccess"
        case message = "Message"
        case response = "ResponseData"
    }
}


struct ResponseList: Decodable {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int?
    let sampleType: [SampleTypeList]?

    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case sampleType = "SampleTypeList"
    }
}


struct SampleTypeList: Decodable {
    let testSampleTypeID: String
    let sampleName: String
    let colourCode: String
    enum CodingKeys: String, CodingKey {
        case testSampleTypeID = "TestSampleTypeId"
        case sampleName = "SampleName"
        case colourCode = "ColourCode"
    }
}

What I did fix, what I didn't like from your sample code:

  • Please name your variables starting with a lowercase, it's convention, and is easier to read (if everyone follow the same convention/standards).
  • Make your code compilable for us. It's not that hard, but if anyone would want to help you, it be much easier for us to just copy/paste your code and test it and not fix everything in it. You'll have better chances to get an answer. There is a Cannot assign value of type 'String' to type 'Int' because packageID is set as an Int and trying to be decoded as a String, there are missing spaces: variable= instead of variable =, etc. It's annoying to have us to fix that to be able to work.
  • You printed the JSON, that's a good point, to test it, there is no need anymore of the Web API Call, see following sample
  • You said that some values can be null, so please provide a JSON with these sample, cut if needed, see the JSON I used as sample, I checked on a JSON online validator that is was valid, and that's all. Since you that that 2 values could be null, I used all possibilities: one with none null, one with one null, one with the other null, and one with both null. Then, for each of these, I put the values as optional.
  • I removed all the init(from decoder:) as they are useless. If you decode each value with let container = try decoder.container(keyedBy:CodingKeys.self); variable = try container.decode(VariableType.self,forKey: .correspondingCodingKeyInTheEnumCase), that code is already done internally by Apple when compiling.

With sample test:

let jsonStr = """
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [{
        "PackageId": 1025,
        "PackageName": "17 OH Progesterone",
        "Price": 0.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [{
            "TestSampleTypeId": "50",
            "SampleName": "Serum",
            "ColourCode": "#FFB500"
        }]
    },
    {
        "PackageId": 1916,
        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
        "Price": 120.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": null
    },
    {
        "PackageId": 1914,
        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
        "Price": 110.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": null,
        "SampleTypeList": [{
            "TestSampleTypeId": "66",
            "SampleName": "24 hrs Urine",
            "ColourCode": "#212DC1"
        }]
    },
    {
        "PackageId": 1913,
        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
        "Price": 12.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": null,
        "SampleTypeList": null
    }
]
}
"""

do {
    let priceList = try JSONDecoder().decode(PriceList.self, from: Data(jsonStr.utf8))
    print(priceList)
} catch {
    print("Error while decoding: \(error)")
}
Sign up to request clarification or add additional context in comments.

6 Comments

thanks for the advise ...i tried as per your answer changed struct as you described ....... let task = URLSession.shared.dataTask(with: request) { (data, response, error) in /*error and response code*/ ..... if let data = data { let json = try? JSONDecoder().decode(PriceList.self, from data) print (json)........Response Status Code 200...fatal error: unexpectedly found nil while unwrapping an Optional value
What is nil? And don’t use try? Use do/try/catch
voila!!! worked finally thank you very much
can you please assist me to how to display the above decoded data in list i have tried but getting blank screen navigation condition defined in viewmodel is working fine but list is not loading
Ask a new question with your code. In the code you gave, you don't use json, it's hard to tell what's wrong. And what are you using to show the data? SwiftUI? A UITableView? I can't guess that in line 34 in the first file sorted alphabetically, it's missing a ";".
|
1

try this, works for me:

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
        SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
    }
}

EDIT:

Here is the code I used to show decoding the given json data works with my answer.

import SwiftUI
import Foundation

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct PriceList {
    let Success: Bool
    let message: String
    let Response: [ResponseList]?
}

extension PriceList: Codable {
    
    enum CodingKeys: String, CodingKey {
        case Success = "IsSuccess"
        case message = "Message"
        case Response = "ResponseData"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        Success = try container.decode(Bool.self,forKey: .Success)
        message = try container.decode(String.self,forKey: .message)
        Response = try container.decode([ResponseList].self,forKey: .Response)
    }
}

struct ResponseList {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int
    let SampleType: [SampleTypeList]?
}

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
        SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
    }
}

struct SampleTypeList {
    let testSampleTypeID, sampleName, colourCode: String
}

extension SampleTypeList:Codable {
    
    enum SampleKeys: String, CodingKey {
        case testSampleTypeID = "TestSampleTypeId"
        case sampleName = "SampleName"
        case colourCode = "ColourCode"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:SampleKeys.self)
        testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )
        sampleName = try container.decode(String.self,forKey: .sampleName )
        colourCode = try container.decode(String.self,forKey: .colourCode)
    }
}

struct ContentView: View {
    
    @State var priceList: PriceList?
    
    var body: some View {
        Text(priceList?.message ?? "no data")
            .onAppear {
                let jsonString  = """
                {
                "IsSuccess": true,
                "Message": "Data Returned",
                "ResponseData": [
                    {
                        "PackageId": 1025,
                        "PackageName": "17 OH Progesterone",
                        "Price": 0.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "50",
                                "SampleName": "Serum",
                                "ColourCode": "#FFB500"
                            }
                        ]
                    },
                    {
                        "PackageId": 1916,
                        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
                        "Price": 120.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 1914,
                        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
                        "Price": 110.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 1913,
                        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
                        "Price": 12.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 936,
                        "PackageName": "24 Hours Urinary Phosphorous",
                        "Price": 15.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 937,
                        "PackageName": "24 Hours Urinary Potassium",
                        "Price": 2.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    }
                   ]
                }
                """
                let data = jsonString.data(using: .utf8)
                priceList = try? JSONDecoder().decode(PriceList.self, from: data!)
                print("\n--> priceList: \(priceList) \n")
            }
    }
}

EDIT-2:

If you can have this in your json data:

"TestPackageGroupId": null,
"SampleTypeList": null
                    

then try this approach to decode your json data:

struct ResponseList {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int? // <--- here optional
    let SampleType: [SampleTypeList]?  // <--- here optional
}

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        // --- here
        testPackageGroupID = try container.decodeIfPresent(Int.self,forKey: .testPackageGroupID)
        SampleType = try container.decodeIfPresent([SampleTypeList].self,forKey: .SampleType)
    }

}

Likewise for any other elements that can have null.

8 Comments

That's not it. If you only decode your data into your Swift object, you don't need Codable, Decodable is enough. Codable is just an alias for Decodable + Encodable. The latter is needed only when you create a JSON out of existing object in your code.
Also init(from decoder:Decoder) throws is redundant if you don't have custom decoding objects and just decode other Decodables into object's properties.
updated my answer to show it decodes the given json data, as requested in the question. Note: you need ResponseList: Codable otherwise PriceList does not conform to protocol Encodable.
You don't need PriceList to be Encodable, Decodable is enough. So, everything might be either full Codable or just Decodable.
I chose Codable since everything else is Codable. If the answer does not work for you, let me know.
|

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.