Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Decodable with inheritance raises an exception

I'm working against Rest API service, where the responses are divided into Base response, and all other responses inherit from it.

I'm trying to building the same structure for my response model classes, using the Decoder interface.

However i'm having issues with the decoding of an inherited class.

I tried to follow this issue: Using Decodable in Swift 4 with Inheritance

But with no luck.

This is the initial structure:

class LoginResponse: BaseResponse{

    var Message: String?

    private enum CodingKeys: String, CodingKey{
        case Message
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        Message = try container.decode(String.self, forKey: .Message)
        let superDecoder = try container.superDecoder()
        try super.init(from: superDecoder)
    }
}

class BaseResponse: Decodable {

    var Status: Int?

    private enum CodingKeys: String, CodingKey{
        case Status
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self) // This line throws the exception
        Status = try container.decode(Int.self, forKey: .Status)
    }
}

Here's how I'm trying to decode:

 let decoder = JSONDecoder()
 let json = "{\"Message\":\"saa\",\"Status\":200}"
 let login = try! decoder.decode(LoginResponse.self, from: json.data(using: .utf8)!)

As I wrote above, this line throws the exception (in BaseResponse class)

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


Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.valueNotFound(Swift.KeyedDecodingContainer<SampleProject.BaseResponse.(CodingKeys in _084835F8074C7E8C5E442FE2163A7A00)>, Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "super", intValue: nil)], debugDescription: "Cannot get keyed decoding container -- found null value instead.", underlyingError: nil))

Not sure how to deal with it.

Thanks in Advance!

like image 453
dor506 Avatar asked Dec 19 '17 11:12

dor506


1 Answers

There is no need to use the superDecoder, you can simply do this (I changed the variable names to lowercase to conform to the naming convention)

class LoginResponse: BaseResponse {

    let message: String

    private enum CodingKeys: String, CodingKey{
        case message = "Message"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        message = try container.decode(String.self, forKey: .message)
        try super.init(from: decoder)
    }
}

class BaseResponse: Decodable {

    let status: Int

    private enum CodingKeys: String, CodingKey{
        case status = "Status"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(Int.self, forKey: .status)
    }
}
  • decoder.decode(BaseResponse.self ... decodes only status
  • decoder.decode(LoginResponse.self ... decodes status and message

And never en-/decode with try!. Handle the error.

like image 146
vadian Avatar answered Nov 18 '22 20:11

vadian