Swift 从兼容的可解码器 class 派生的 class 可解码器失败(JSONDecoder 用于解码)

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

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]
}

此处:

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

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

没有正确解码。 cards 和 activeCardId 保持为零。当我将 public class CardListResponse: BaseResponse 更改为 public class CardListResponse: Decodable the cards 和 activeCardId 解析得很好(但显然我没有从 base class).

我该如何应对?

如果问题不清楚这里的示例 json:

我的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)
    }
}

你需要在 subclass 中实现 init(from decoder: Decoder) 并调用 superclass 初始化,尽管你不需要为 superclass[ 实现它=16=]

init for CardListResponse,注意对 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)
}

另一种选择是完全跳过继承并改用组合。在 subclass 中,你为 CodeAndDetails 数组创建了一个 属性,这样你就不再需要特殊的 init 方法了。

然后可以将

CardListResponse 更改为以下,并且可以删除超级 class。在长运行中我认为这是一个比较有吸引力的解决方案。

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"
    }
}