I am trying to parse JSON using Codable protocol but getting exception. This exception is coming due to null value in response which of nestedkeyedcontainer for second response.
Its working fine for below json response
let jsonStr = """
{
"response": {
"PURCHASE_FREE_RESPONSE": {
"TOPUP": null,
"TOTAL_PAYMENT_AMOUNT": "0",
"TAX": null,
"VAT": null,
........
"CUSTOM_FIELD_2": null,
"CUSTOM_FIELD_3": null,
"FEE": null
}
},
"API-Code": "ttttt",
"statuscode": 200,
"statusMessage": "SUCCESS",
"respCode": {
"ERROR_CODE": "0",
"ERROR_DESC": "Success"
}
}
"""
//=========Second Response===============
let jsonStr = """
{
**"response": null,**
"API-Code": null,
"statuscode": 500,
"statusMessage": "Undefined index: HOUSE_NO",
**"respCode": null**
}
"""
My Model looks like:-
struct SimPurResponse:Decodable{
let topUp:String
let totalPaymentAmt:String
let transId:String
let apiCode:String
let statuscode:Int
let statusMessage:String
let errorCode:String
let errorDesc:String
private enum ResponseOuter : String, CodingKey {
case response
case apiCode = "API-Code"
case statuscode = "statuscode"
case respCode
case statusMessage = "statusMessage"
}
enum RespCodeKeys : String, CodingKey {
case errorCode = "ERROR_CODE"
case errorDesc = "ERROR_DESC"
}
private enum Response : String, CodingKey {
case PURCHASE_FREE_SIM_RESPONSE
}
private enum purchaseFreeSimResInfo : String, CodingKey {
case topUp = "TOPUP"
case totalPaymentAmt = "TOTAL_PAYMENT_AMOUNT"
case transId = "TRANSACTION_ID"
case tax = "TAX"
case vat = "VAT"
case shipingAmt = "SHIPPING_AMOUNT"
}
init(from decoder :Decoder) throws {
let container = try decoder.container(keyedBy: ResponseOuter.self)
self.apiCode = try container.decodeIfPresent(String.self, forKey: .apiCode) ?? ""
self.statuscode = try container.decodeIfPresent(Int.self, forKey: .statuscode) ?? 600
self.statusMessage = try container.decodeIfPresent(String.self, forKey: .statusMessage) ?? ""
let respCodeCont = try container.nestedContainer(keyedBy: RespCodeKeys.self, forKey: .respCode)
self.errorCode = try respCodeCont.decodeIfPresent(String.self, forKey: .errorCode) ?? ""
self.errorDesc = try respCodeCont.decodeIfPresent(String.self, forKey: .errorDesc) ?? ""
let purcCont = try container.nestedContainer(keyedBy: Response.self, forKey: .response)
let purSimCont = try purcCont.nestedContainer(keyedBy: purchaseFreeSimResInfo.self, forKey: .PURCHASE_FREE_SIM_RESPONSE)
self.topUp = try purSimCont?.decodeIfPresent(String.self, forKey: .topUp) ?? ""
self.totalPaymentAmt = try purSimCont?.decodeIfPresent(String.self, forKey: .totalPaymentAmt) ?? ""
self.transId = try purSimCont?.decodeIfPresent(String.self, forKey: .transId) ?? ""
self.tax = try purSimCont?.decodeIfPresent(String.self, forKey: .tax) ?? ""
self.vat = try purSimCont?.decodeIfPresent(String.self, forKey: .vat) ?? ""
self.shipingAmt = try purSimCont?.decodeIfPresent(String.self, forKey: .shipingAmt) ?? ""
}
}
let dataJosn = Data(jsonStr.utf8)
do {
let simDetail = try JSONDecoder().decode(SimPurResponse.self, from: dataJosn)
print(simDetail)
}catch let error {
print(error)
}
I am getting problem in handling both situation. Above complete code
Just change your 'try' to 'try?' when accessing nestedContainer in the decoder initializer block, and add optional chaining where needed:
init(from decoder :Decoder) throws {
let data = try decoder.container(keyedBy: ResponseOuter.self)
self.apiCode = try data.decodeIfPresent(String.self, forKey: .apiCode) ?? ""
self.statuscode = try data.decodeIfPresent(Int.self, forKey: .statuscode) ?? 600
self.statusMessage = try data.decodeIfPresent(String.self, forKey: .statusMessage) ?? ""
let respCodeCont = try? data.nestedContainer(keyedBy: RespCodeKeys.self, forKey: .respCode)
self.errorCode = try respCodeCont?.decodeIfPresent(String.self, forKey: .errorCode) ?? ""
self.errorDesc = try respCodeCont?.decodeIfPresent(String.self, forKey: .errorDesc) ?? ""
let purcCont = try? data.nestedContainer(keyedBy: Response.self, forKey: .response)
let purSimCont = try purcCont?.nestedContainer(keyedBy: purchaseFreeSimResInfo.self, forKey: .PURCHASE_FREE_SIM_RESPONSE)
self.topUp = try purSimCont?.decodeIfPresent(String.self, forKey: .topUp) ?? ""
self.totalPaymentAmt = try purSimCont?.decodeIfPresent(String.self, forKey: .totalPaymentAmt) ?? ""
self.transId = try purSimCont?.decodeIfPresent(String.self, forKey: .transId) ?? ""
self.tax = try purSimCont?.decodeIfPresent(String.self, forKey: .tax) ?? ""
self.vat = try purSimCont?.decodeIfPresent(String.self, forKey: .vat) ?? ""
self.shipingAmt = try purSimCont?.decodeIfPresent(String.self, forKey: .shipingAmt) ?? ""
self.itgTransId = try purSimCont?.decodeIfPresent(String.self, forKey: .itgTransId) ?? ""
self.chanelTransId = try purSimCont?.decodeIfPresent(String.self, forKey: .chanelTransId) ?? ""
self.paymentResponse = try purSimCont?.decodeIfPresent(String.self, forKey: .paymentResponse) ?? ""
self.apmResponse = try purSimCont?.decodeIfPresent(String.self, forKey: .apmResponse) ?? ""
self.applicationType = try purSimCont?.decodeIfPresent(String.self, forKey: .applicationType) ?? ""
self.nameorPageId = try purSimCont?.decodeIfPresent(String.self, forKey: .nameorPageId) ?? ""
self.url = try purSimCont?.decodeIfPresent(String.self, forKey: .url) ?? ""
self.customField1 = try purSimCont?.decodeIfPresent(String.self, forKey: .customField1) ?? ""
self.customField2 = try purSimCont?.decodeIfPresent(String.self, forKey: .customField2) ?? ""
self.customField3 = try purSimCont?.decodeIfPresent(String.self, forKey: .customField3) ?? ""
self.fee = try purSimCont?.decodeIfPresent(String.self, forKey: .fee) ?? ""
}
Hope this helps. Cheers.