如何解决使用从函数返回的类类型对 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.self
、Item2Class.self
或 Item3Class.self
,依此类推。但是你知道无论它返回什么,它都是 ContentItem
的子类,所以至少,你可以将它分配给一个 ContentItem
类型的变量,所以 content
只能是类型ContentItem.Type
。
更准确地说,getItemClass
返回ContentItem.Type
,它变成了contentItemType
的推断类型,当你将它传递给decode
方法时,泛型参数{{1 }} 被推断为 T
。 ContentItem
的返回类型也是 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 举报,一经查实,本站将立刻删除。