Swift Decodable fails for a class derived from a decodable compliant class (JSONDecoder is used for decoding)

只谈情不闲聊 提交于 2019-12-13 03:30:32

问题


public struct CodeAndDetails: Codable {
    public let html: String
    public var code: String

    private enum CodingKeys: String, CodingKey {
        case html = "DETAILS", code = "CODE"
    }

    public func getMessage(font: UIFont) -> NSAttributedString? {
        let res = NSAttributedString(html: html, font: font)
        return res
    }
}

public class BaseResponse: Decodable {

    enum CodingKeys: String, CodingKey {
        case successDetails = "Success"
    }
    public let successDetails: [CodeAndDetails]
}

here:

public class CardListResponse: BaseResponse {
    public var cards: [DebitCard]?
    public var activeCardId: Int?

    enum CodingKeys: String, CodingKey {
        case cards = "row"
        case activeCardId = "CurrentActiveId"
    }
}

Does not decode properly. cards and activeCardId stay nil. When I change public class CardListResponse: BaseResponse to public class CardListResponse: Decodable the cards and activeCardId parse just fine (but obviously I get no payload from base class).

How could I cope with this?

In case it's not clear from the question here's sample json:

My JSON

{  
   "row":[  
      {  
         "PicPath":"MC"
      },
      {  
         "IsDefault":"",
         "PicPath":"VISA"
      }
   ],
   "CurrentActiveId":17504322,
   "Success":[  
      {  
         "DETAILS":"\u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e",
         "CODE":"1"
      }
   ]
}
public struct DebitCard: Decodable
{
    public let accountContractId: Int?
    public let last8: String?
    public let balanceString: String?

    public let currName: String?
    public let iCardId: Int?
    public let isMain: Bool?
    public let cardNameWithCurrencyCode: String?
    public let card4: String?

    public let title: String?
    public let picPath: String?

    enum CodingKeys: String, CodingKey {
        case accountContractId = "AcntContractId"
        case expire = "Expire"
        case last8 = "Card8"
        case balanceString = "Balance"

        case currName = "CurrName"
        case iCardId
        case isMainString = "isMain"
        case cardNameWithCurrencyCode = "CardName"

        case card4 = "Card4"
        case title = "CrdTInfo" 
        case picPath = "PicPath"

    }
    public init(from decoder: Decoder) throws
    {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        accountContractId = try values.decode(Int.self, forKey: .accountContractId)
        last8 = try values.decode(String.self, forKey: .last8)
        balanceString = try values.decode(String.self, forKey: .balanceString)
        currName = try values.decode(String.self, forKey: .currName)
        iCardId = try values.decode(Int.self, forKey: .iCardId)
        let isMainString = try values.decode(String.self, forKey: .isMainString)
        isMain = isMainString.toBool
        cardNameWithCurrencyCode = try values.decode(String.self, forKey: .cardNameWithCurrencyCode)
        card4 = try values.decode(String.self, forKey: .card4)
        title = try values.decode(String.self, forKey: .title)
        picPath = try values.decode(String.self, forKey: .picPath)
    }
}

回答1:


You need to implement init(from decoder: Decoder) in the subclass and call the superclass init although you don't need to implement it for the superclass

init for CardListResponse, notice the call to super.init

required public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    cards = try container.decode([DebitCard].self, forKey: .cards)
    activeCardId = try container.decode(Int.self, forKey: .activeCardId)
    try super.init(from: decoder)
}

Another option is to skip inheritance altogether and use composition instead. In the subclass you make a property for the CodeAndDetails array instead, this way you won't need a special init method anymore.

CardListResponse can then be changed to below and the super class can be removed. In the long run I think this is a more attractive solution.

public class CardListResponse: Decodable {
    public var cards: [DebitCard]?
    public var activeCardId: Int?
    public var details: [CodeAndDetails]

    private enum CodingKeys: String, CodingKey {
        case cards = "row"
        case activeCardId = "CurrentActiveId"
        case details = "Success"
    }
}


来源:https://stackoverflow.com/questions/57146148/swift-decodable-fails-for-a-class-derived-from-a-decodable-compliant-class-json

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!