how to parse this complex nested Json

341 views Asked by At

The Json is valid but I m getting nil with below code and struct for the returned json.

problem encountered:

  1. at JSonDecoder.decode() : it returned this error msg:

    keyNotFound(CodingKeys(stringValue: "items", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "items", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: "items", intValue: nil) ("items").", underlyingError: nil))

  2. How to get these a) image b) location from the Json

Thanks

here the code

func getJsonMapData(){
        
        guard let mapUrl = URL(string: "https://xxxxxx/traffic-images") else { return }
        
        URLSession.shared.dataTask(with: mapUrl) { (data, response, error) in
        
            guard error == nil else { return}

            guard let data = data else { return}
         
            //- problem: 

            do {

                let LocationArrDict = try JSONDecoder().decode([String:[Location]].self, from: data)else{
                
                print(LocationArrDict)              
  
            } catch {

              print(error)
            }
           
        }.resume()
    }



//------------- return Json String:

{
   "items":[
      {
         "timestamp":"2020-12-05T08:45:43+08:00",
         "cameras":[
            {
               "timestamp":"2020-11-05T08:42:43+08:00",
               "image":"https://xxxxxxxxx/traffic-images/2020/12/2ab06cd8-4dcf-434c-b758-804e690e57db.jpg",
               "location":{
                  "latitude":1.29531332,
                  "longitude":103.871146
               },
               "camera_id":"1001",
               "image_metadata":{
                  "height":240,
                  "width":320,
                  "md5":"c9686a013f3a2ed4af61260811661fc4"
               }
            },
            {
               "timestamp":"2020-11-05T08:42:43+08:00",
               "image":"https://xxxxxxxxxx/traffic-images/2020/12/9f6d307e-8b05-414d-b27d-bf1414aa2cc7.jpg",
               "location":{
                  "latitude":1.319541067,
                  "longitude":103.8785627
               },
               "camera_id":"1002",
               "image_metadata":{
                  "height":240,
                  "width":320,
                  "md5":"78060d8fbdd241adf43a2f1ae5d252b1"
               }
             },

                    ........

            {
               "timestamp":"2020-12-05T08:42:43+08:00",
               "image":"https://xxxxxx/traffic-images/2020/12/98f64fe6-5985-4a8a-852f-0be24b0a6271.jpg",
               "location":{
                  "latitude":1.41270056,
                  "longitude":103.80642712
                },
                 "camera_id":"9706",
                 "image_metadata":{
                  "height":360,
                  "width":640,
                  "md5":"f63d54176620fa1d9896fa438b3cc753"
                }
            }
          ]
        }
  
       ],
  
      "api_info":{
         "status":"healthy"
       }
}



//------------ struct for the return Json result:


// MARK: - Location

struct Location: Codable {
    let items: [Item]
    let apiInfo: APIInfo

    enum CodingKeys: String, CodingKey {
        case items
        case apiInfo = "api_info"
    }
}

// MARK: - APIInfo
struct APIInfo: Codable {
    let status: String
}

// MARK: - Item
struct Item: Codable {
    let timestamp: Date
    let cameras: [Camera]
}

// MARK: - Camera
struct Camera: Codable {
    let timestamp: Date
    let image: String
    let location: LocationClass
    let cameraID: String
    let imageMetadata: ImageMetadata

    enum CodingKeys: String, CodingKey {
        case timestamp, image, location
        case cameraID = "camera_id"
        case imageMetadata = "image_metadata"
    }
}

// MARK: - ImageMetadata
struct ImageMetadata: Codable {
    let height, width: Int
    let md5: String
}

// MARK: - LocationClass
struct LocationClass: Codable {
    let latitude, longitude: Double
}



``
2

There are 2 answers

4
vadian On BEST ANSWER

Error #1: The type to be decoded is wrong it must be Location.self.
Error #2: To decode the ISO date as Date you have to add the .iso8601 date decoding strategy.

do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    let locationArrDict = try decoder.decode(Location.self, from: data)
    print(locationArrDict)              

} catch {
   print(error)
}

And you could decode the strings representing an URL directly to URL

0
gasho On

You have to create your data models and describe them as the response you expect to receive. I will just give you a brief example based on your json how you would decode it.

First of all in order to decode a JSON using JSONDecoder your models have to conform to Decodable protocol. Then you have to create those models.

struct Item: Decodable {
    let timestamp: Date
//    let cameras: [...]
}

struct ApiInfo: Decodable {
    enum Status: String, Decodable {
        case healthy
    }
    
    let status: Status
}

struct Response: Decodable {
    let items: [Item]
    let apiInfo: ApiInfo
}

Then you have to configure your JSONDecoder and decode that JSON. As we can see clearly, your JSON uses snake_case naming convention and that is not the default one for JSONDecoder so you have to set it. Same also applies for the dates - it is used iso8601 standard of representation.

let jsonDecoder = JSONDecoder()
jsonDecoder.dateDecodingStrategy = .iso8601
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase

Finally, you have you decode it.

do {
    let response = try jsonDecoder.decode(Response.self, from: jsonData)
    print(response)
} catch {
    print(error)
}