微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用从函数返回的类类型对 Codable 进行解码实际上会返回一个以超类为类型的项目

如何解决使用从函数返回的类类型对 Codable 进行解码实际上会返回一个以超类为类型的项目

我为 json 响应编写了一个自定义解码器,其中我有一个内容”字段,可以将其解码为各种不同的类,所有这些类都继承自同一个 ContentItem 超类。我还有一个返回类类型的函数,我在我的解码器中使用它(我知道可能有不同的方法来做到这一点,但这不是问题):

    init(from decoder: Decoder) throws {
        ...
        guard let type = type,let contentItemType = getItemClass(from: type) else { return }
        content = try container.decode(contentItemType,forKey: .content)
    }

    func getItemClass(from type: ProductContentType) -> ContentItem.Type? {...}

ProductContentType 是一个字符串枚举。 getItemClass 函数返回正确的类,我在调试时检查过:

    getItemClass(from: .type1) === Item1Class.self   //<-- this returns true

问题如下:

    guard let type = type,let contentItemType = getItemClass(from: type) else { return }
    content = try container.decode(contentItemType,forKey: .content)
    // ^ In this case content is kind of class ContentItem
    let downcastContent = content as? Item1Class // <- downcastContent is nil

    content = try container.decode(Item1Class.self,forKey: .content)
    // ^ In this case content is kind of class Item1Class
    let downcastContent = content as? Item1Class // this works 

在第一种情况下使用内容进行向下转换返回 nil,但这对我来说没有意义。我还注意到,在解码项目时,子类中的 init(from: Decoder) 永远不会被调用。 应该是这样吗?我期待两个解码都返回一个 Item1Class 类的内容。在第一种情况下,我在解码过程中遗漏了什么吗?

解决方法

应该是这样吗?

是的。请注意,您并不确切知道解码的类型是什么 - getItemClass 可能返回 Item1Class.selfItem2Class.selfItem3Class.self,依此类推。但是你知道无论它返回什么,它都是 ContentItem 的子类,所以至少,你可以将它分配给一个 ContentItem 类型的变量,所以 content 只能是类型ContentItem.Type

更准确地说,getItemClass返回ContentItem.Type,它变成了contentItemType的推断类型,当你将它传递给decode方法时,泛型参数{{1 }} 被推断为 TContentItem 的返回类型也是 decode,因此 T 的类型为 content

然而,就像 ContentItem 的类型为 contentItemType,它的值实际上是一个 ContentItem.Type,即使 Item1Class.self 的类型为 content ,它实际上有一个对 ContentItem 实例的引用。您可以通过显示转换成功来显示这一点:

Item1Class

或者您可以直接检查 let item1 = content as! Item1Class // this succeeds 的运行时类型:

content

type(of: content) == Item1Class.self / true 实际查看您传递给它的 JSONDecoder。该参数不仅仅用于推断 contentItemType

这是一个最小的可重现示例:

T

请注意,在上面的示例中,子类 class Foo : Decodable { let a: String } class Bar: Foo { enum CodingKeys: CodingKey { case b } let b: String required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) b = try container.decode(String.self,forKey: .b) try super.init(from: decoder) } } let json = """ {"a": "Foo","b": "Bar"} """.data(using: .utf8) func getItemClass() -> Foo.Type { Bar.self } let type = getItemClass() let fooButItsActuallyBar = try JSONDecoder().decode(type,from: json!) let bar = fooButItsActuallyBar as! Bar // this succeeds print(bar.a,bar.b) 覆盖了实现自定义解码所需的初始化程序,因为不会为子类 (See also) 生成自动生成的实现。如果您没有覆盖它,您将看到子类的属性未初始化。这可能是您认为 Bar 解码 decode 实例的另一个原因。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。