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

Samarth Kejriwal
  • 1,168
  • 2
  • 15
  • 30

1 Answers1

1

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)
}
André Slotta
  • 13,774
  • 2
  • 22
  • 34