对于从可解码兼容类派生的类(JSONDecoder 用于解码),Swift Decodable 失败 [英] Swift Decodable fails for a class derived from a decodable compliant class (JSONDecoder is used for decoding)

查看:49
本文介绍了对于从可解码兼容类派生的类(JSONDecoder 用于解码),Swift Decodable 失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

未正确解码.卡和 activeCardId 保持为零.当我将公共类 CardListResponse: BaseResponse 更改为公共类 CardListResponse: 可解码卡片和 activeCardId 解析就好了(但显然我没有从基类中获得有效负载).

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).

我该如何应对?

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

In case it's not clear from the question here's sample 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)
    }
}

推荐答案

你需要在子类中实现 init(fromdecoder: Decoder) 并调用超类 init 虽然你不需要为超类实现它

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,注意调用 super.init

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

<小时>

另一种选择是完全跳过继承并使用组合代替.在子类中,您为 CodeAndDetails 数组创建了一个属性,这样您就不再需要特殊的 init 方法.


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 然后可以更改为下面,并且可以删除超类.从长远来看,我认为这是一个更有吸引力的解决方案.

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

这篇关于对于从可解码兼容类派生的类(JSONDecoder 用于解码),Swift Decodable 失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆