Search code examples
iosswiftalamofirecodable

Not able to parse data using Codable and Alamofire 3.0


I am unable to parse my json response data using decodable and alamofire 3.0. I am getting the following error.

Error keyNotFound(CodingKeys(stringValue: "server_time", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"server_time\", intValue: nil) (\"server_time\").", underlyingError: nil))

Here is my code.

Parsing Code

  struct NewAucListJSON_Modal : Codable
{
    let serverTime : String?
    let error: Bool?

    let display_name: String?
    let list_id: Int?
    let fk_com_id: Int?

    let numFound : Int?

    enum CodingKeys: String, CodingKey {

        case data = "data"
        case response = "response"
        case numFound = "numFound"
        case docs = "docs"

        case display_name = "display_name", list_id = "list_id", fk_com_id = "fk_com_id"

        case serverTime = "server_time"
        case error = "error"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let response_data = try container.nestedContainer(keyedBy:
            CodingKeys.self, forKey: .data)

        self.serverTime = try response_data.decode(String.self, forKey: .serverTime)
        self.error = try response_data.decode(Bool.self, forKey: .error)

        let response = try response_data.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
        let docs = try response.nestedContainer(keyedBy: CodingKeys.self, forKey: .docs)

        display_name = try docs.decode(String.self, forKey: .display_name)
        list_id = try docs.decode(Int.self, forKey: .list_id)
        fk_com_id = try docs.decode(Int.self, forKey: .fk_com_id)

        self.numFound = try response.decode(Int.self, forKey: .numFound)

    }
    func encode(to encoder: Encoder) throws {

    }
}

JSON Response

{
    "data": {
        "responseHeader": {
            "status": 0,
            "QTime": 0,
            "params": {
                "q": "type:*",
                "front_id:[1 TO 7] OR market:Nafed": "",
                "fq": ["status:[100 TO 190]", "!template_id:(\"-40\" \"-50\")"],
                "sort": "status DESC"
            }
        },
        "response": {
            "numFound": 7,
            "start": 0,
            "docs": [{
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }, {
                "fk_com_id": 6,
                "list_id": 3089,
                "display_name": "GDSMP-3089"
            }]
        }
    },
    "error": false,
    "message": "Data Listed.",
    "recordsFiltered": 0,
    "recordsTotal": 0,
    "server_time": "2019-01-08 15:03:28"
}

If I used different structs for nested containers, then parsing code becomes:

   struct AucListInfo : Codable
{
    let docs : [AucListDoc]
    let numFound : Int

    enum CodingKeys: String, CodingKey {
        case response = "response"
        case docs = "docs"
        case numFound = "numFound"
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        var response = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
        try response.encode(self.docs, forKey: .docs)
        try response.encode(self.numFound, forKey: .numFound)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let response = try container.nestedContainer(keyedBy:
            CodingKeys.self, forKey: .response)
        self.docs = try response.decode([AucListDoc].self, forKey: .docs)
        self.numFound = try response.decode(Int.self, forKey: .numFound)
    }
}

struct AucListDoc : Codable
{
    let display_name: String?
    let list_id: Int?
    let fk_com_id: Int?

    private enum CodingKeys: String, CodingKey {

        case display_name = "display_name", list_id = "list_id", fk_com_id = "fk_com_id"
    }

    init(display_name: String, list_id: Int, fk_com_id: Int) {

        self.display_name = display_name
        self.list_id = list_id
        self.fk_com_id = fk_com_id

    }

    init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)

        display_name = try container.decode(String.self, forKey: .display_name)
        list_id = try container.decode(Int.self, forKey: .list_id)
        fk_com_id = try container.decode(Int.self, forKey: .fk_com_id)

    }
}

struct AucListJSON_Modal: Codable
{
//    let data: AucListResponse?
    let server_time : String
    let error: Bool
    let response : AucListInfo

    enum CodingKeys: String, CodingKey {
        case data = "data"
        case response = "response"
        case server_time = "server_time"
        case error = "error"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let response = try container.nestedContainer(keyedBy:
            CodingKeys.self, forKey: .data)
        self.server_time = try response.decode(String.self, forKey: .server_time)
        self.error = try response.decode(Bool.self, forKey: .error)
        self.response = try response.decode(AucListInfo.self, forKey: .response)
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        var response = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .data)
        try response.encode(self.server_time, forKey: .server_time)
        try response.encode(self.error, forKey: .error)
        try response.encode(self.response, forKey: .response)
    }
}

Please help me with the mistake


Solution

  • Not completely sure if I understand you correctly but I think this is what you're looking for. Kinda... :)

    struct JSONResponse: Decodable {
    
        struct AucListDoc: Decodable {
            // I renamed some properties to suit conventions
            let displayName: String
            let listId: Int
            let fkComId: Int
        }
    
        enum CodingKeys: String, CodingKey {
            // don't need any key mapping here since the differences
            // between the json's key names and the structs' properties'
            // names are handled via the 'JSONDecoder's 'keyDecodingStrategy'
            // (see the bottom of the answer)
            case serverTime
            case error
    
            case data
            case response
    
            case numFound
            case docs
        }
    
        // I only added the properties you seem to care about here
        let serverTime: String
        let error: Bool
        let numFound: Int
        let docs: [AucListDoc]
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
    
            // you can immediately get 'serverTime' and 'error'
            // from the outermost container
            serverTime = try container.decode(String.self, forKey: .serverTime)
            error = try container.decode(Bool.self, forKey: .error)
    
            // since 'numFound' and 'docs' are contained in the nested
            // 'response' container which itself is contained in the nested
            // 'data' container you first have to get those nested containers
            let data = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .data)
            let response = try data.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
    
            // now you can get 'numFound' and 'docs' from the nested
            // 'response' container
            numFound = try response.decode(Int.self, forKey: .numFound)
            docs = try response.decode([AucListDoc].self, forKey: .docs)
        }
    
    }
    

    You can decode the jsonData like this (do not forget the JSONDecoders keyDecodingStrategy):

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