【问题标题】:Swift - JSONDecoder - decoding generic nested classesSwift - JSONDecoder - 解码通用嵌套类
【发布时间】:2019-01-03 11:54:40
【问题描述】:

我对 Swift 很陌生,我正在尝试创建一个类树来为我的数据建模。我想使用 JSONEncoder 和 JSONDecoder 来发送和接收对象。我在解码另一个对象(嵌套)中的泛型类时遇到问题,因为在 init(from: decoder) 方法中我无法访问可以帮助我的其他属性。

在我的代码中:

NestedSecondObject 扩展了 NestedObjects,后者扩展了 Codable - NestedObject 可以通过 NesteThirtObject 等进行扩展...

Contact 扩展了 Object1,后者扩展了 Codable; Contact 包含一个 NestedObject 类型(在运行时可以是 NestedObject 的任何子类)

因为默认情况下 JSONEncoder 和 JSONDecoder 不支持继承,所以我重写了方法“encode”和 init(from: decoder),如下所述:Using Decodable in Swift 4 with Inheritance

我的代码是:

class NestedSecondObject: NestedObject {
    var subfield2: Int?

    private enum CodingKeys : String, CodingKey {
        case subfield2
    }

    override init() { super.init() }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(subfield2, forKey: .subfield2)
    }

    required init(from decoder: Decoder) throws
    {
        try super.init(from: decoder)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.subfield2 = try values.decode(Int.self, forKey: .subfield2)

    }

}


class Contact:Object1 {
    var name: String = ""
    var age: Int = 0
    var address: String = ""
//    var className = "biz.ebas.platform.generic.shared.EContactModel"
    var nestedObject:NestedObject?

    private enum CodingKeys : String, CodingKey {
        case name,age,address,nestedObject
    }

    override init() { super.init() }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(age, forKey: .age)
        try container.encode(address, forKey: .address)
        try container.encode(nestedObject, forKey: .nestedObject)
    }

    required init(from decoder: Decoder) throws
    {
        try super.init(from: decoder)
        print(type(of: self))
        print(type(of: decoder))

        let values = try decoder.container(keyedBy: CodingKeys.self)
        print(type(of: values))


        self.name = try values.decode(String.self, forKey: .name)
        self.age = try values.decode(Int.self, forKey: .age)
        self.address = try values.decode(String.self, forKey: .address)
        self.nestedObject = try values.decodeIfPresent(???.self, forKey: .nestedObject)  // HERE i need to know what ???.self is
    }
}

解码是:

let jsonDec = JSONDecoder()
let jsonData = json?.data(using: .utf8)
let decodedContact: Contact = try jsonDec.decode(Contact.self, from: jsonData!)

所以,基本上,当我向服务器发出请求时,我知道我收到了什么类型(假设我请求 NestedSecondObject),但是我如何将它传递给方法“init(来自解码器:解码器)”?

我尝试扩展 JSONDecoder 类并添加一个像这样的简单属性:

class EJSONDecoder: JSONDecoder {

    var classType:AnyObject.Type?

}

但是在所需的init(from decoder:Decoder)方法中,解码器的类型不是EJSONDecoder,是_JSONDecoder,所以我无法访问decoder.classType属性。

任何人都可以提供解决方案或某种解决方法吗? 谢谢!

【问题讨论】:

  • 一般我会说你必须检查嵌套对象的“原始”JSON数据以确定它是什么类型,然后使用这种类型进行解码。

标签: json swift jsondecoder


【解决方案1】:

新答案

你可以给解码器一个userInfo数组,你可以在其中存储你想在解码过程中使用的东西。

let decoder = JSONDecoder()
decoder.userInfo = [.type: type(of: NestedSecondObject.self)]
let decodedContact: Contact = try! decoder.decode(Contact.self, from: json)

extension CodingUserInfoKey {
    static let type = CodingUserInfoKey(rawValue: "Type")!
}

然后在解码时使用:

switch decoder.userInfo[.type]! {
case let second as NestedSecondObject.Type:
    self.nestedObject = try values.decode(second, forKey: .nestedObject)
case let first as NestedObject.Type:
    self.nestedObject = try values.decode(first, forKey: .nestedObject)
default:
    fatalError("didnt work")
}

遗憾的是,我还没有找到跳过切换的方法。

旧答案:

解码为

NestedSecondObject.self

如果在 catch 内解码失败,则使用

NestedObject.self

。如果它甚至无法解码为基本类型,请不要捕获 NestedObject-decoding 导致您想要失败的原因。

【讨论】:

  • 问题是我不知道 NestedSecondClass 是实际的类。最后,我想我会从服务器发送json字符串响应中的类
  • @AndreiF 如果您不想尝试所有可能的类型,最好使用解码器的.userInfo 属性。
  • @AndreiF 我用一个例子更新了答案,你可以如何应用 userInfo 来做你想做的事。
  • 我也是这样做的。但我不明白,为什么我不能用这个? : self.nestedObject = try values.decode(decoder.userInfo[.type]!, forKey: .nestedObject) - 我的意思是,我怎样才能避免 switch / if 语句?
  • 因为它是一个通用函数 decode 可能必须在编译时知道它应该返回什么。它无法从参数中获取该信息,因为它可以是任何类型。而且它一开始就不知道给定的类型是可解码的,它必须在编译时知道这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-09
  • 1970-01-01
相关资源
最近更新 更多