Search code examples
jsonswiftjsondecoderjsonserializer

JSON decoder issue. How I can parse these data?


everyone. I am making an app for iOS. I use an API that allows to fetch city's sights as JSON.

Here is an example of the data:

{
   "type":"FeatureCollection",
   "features":[
      {
         "type":"Feature",
         "id":"7281004",
         "geometry":{
            "type":"Point",
            "coordinates":[]
         },
         "properties":{
            "xid":"N5661720423",
            "name":"М. В. Захарову",
            "dist":70.53949268,
            "rate":1,
            "osm":"node/5661720423",
            "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
         }
      },
... 499 objects more

My solution for parsing in SWIFT is

import Foundation

struct SightResponse: Decodable, Hashable {
    let allInfo = [String]()
    enum CodingKeys: String, CodingKey{
        case features
        case type
        
        enum FeatureKey: String, CodingKey{
            case type
            case id
        }
    }
    
    
}
extension SightResponse{
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let type = try values.decode(String.self, forKey: .type)
        let ContainsFeatures = values.contains(.features)
        let features = try values.decode([String: String].self, forKey: .features)
        //skips the features line
    }
}

Also, at this line I tried different variations of data type such as let features = try values.decode(String.self, forKey: .features) or let features = try values.decode([String].self, forKey: .features)

I am really confused.

I tried to change the parsing data type, but there is nothing new. I am trying to understand what I am doing wrong with my JSON data, and how I can understand what data type should be used in the future?


Solution

  • 1. Declare your Codable data types to parse the JSON objects

    struct FeatureCollection: Codable {
        let type: String
        let features: [Feature]
    }
    
    struct Feature: Codable {
        let type: String
        let id: String
        let geometry: Geometry
        let properties: Properties
    }
    
    struct Geometry: Codable {
        let type: String
        let coordinates: [Double]
    }
    
    struct Properties: Codable {
        let xid: String
        let name: String
        let dist: Double
        let rate: Int
        let osm: String
        let kinds: String
    }
    

    2. Prepare some JSON mock data

    // Some dummy data with 2 features in the array
    
    let jsonString = """
    {
        "type":"FeatureCollection",
        "features":[
            {
                "type":"Feature",
                "id":"1",
                "geometry":{
                    "type":"Point",
                    "coordinates":[]
                },
                "properties":{
                    "xid":"N5661720423",
                    "name":"М. В. Захарову",
                    "dist":70.53949268,
                    "rate":1,
                    "osm":"node/5661720423",
                    "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
                }
            },
            {
                "type":"Feature",
                "id":"2",
                "geometry":{
                    "type":"Point",
                    "coordinates":[]
                },
                "properties":{
                    "xid":"N5661720423",
                    "name":"М. В. Захарову",
                    "dist":70.53949268,
                    "rate":1,
                    "osm":"node/5661720423",
                    "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
                }
            }
        ]
    }
    """
    

    3. Decode the mock JSON data into Swift object

    // Decoding
    
    let jsonData = jsonString.data(using: .utf8)!
    let decoder = JSONDecoder()
    let featureCollection = try! decoder.decode(FeatureCollection.self, from: jsonData)
    

    Extension

    to use a init(from decoder: Decoder) constructor, look at this:

    struct FeatureCollection: Codable {
        let type: String
        let features: [Feature]
        
        init(
            type: String,
            features: [Feature]
        ) {
            self.type: String, = type
            self.features: [Feature] = features
        }
        
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
    
            type = try container.decodeIfPresent(String.self, forKey: .type)
            features = try container.decodeIfPresent([Feature].self, forKey: .features)
        }
    
        enum CodingKeys: String, CodingKey {
            case type
            case features
        }
    }