KeyedDecodingContainer如何实现decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T: Decodable

How does KeyedDecodingContainer implement decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T: Decodable

我正在试验 Decodable,无法弄清楚编译器如何为特定的 Decodable 实例(例如 Book)合成 decode(_ type: Decodable.Protocol, forKey key: CodingKey) 函数。

struct Book: Codable {
    var title: String
    var pages: Int
    var author: String
    var coauthor: String
}
struct Bookstore: Codable {
    var book: Book
    var owner: String
}

我试图自己编写一个 decode(_:forKey:) 函数,这样我就可以更好地了解编译器是如何综合它的,但我完全一头雾水。我开始为 Book.Type 重载 decode(_:forKey:) 函数,但就我所知。

extension KeyedDecodingContainer {
    func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
         print("I am about to decode a Book from the data keyed by \(key.stringValue)")

         // I have no clue what to put here

    }
}

如果有人能帮助我完成这个实现,我将不胜感激,因为它将帮助我更好地理解解码过程。

提前致谢!

编辑:

到目前为止我尝试过的:

extension KeyedDecodingContainer {
    func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
        print("I am about to decode a Book from the data keyed by \(key.stringValue)")

        let decoder = try self.superDecoder(forKey: key as! K)
        return try Book(from: decoder)
    }
}

这行得通,但我不知道 as! K 到底在做什么,也不知道 superDecoder(forKey:) 做了什么。另外我不确定在这种情况下使用强制施法是否被认为是有风险的。有更安全的方法吗?

我认为你在这里误解了一些东西,编译器没有合成任何东西。 decode 泛型 ,泛型类型参数 T 被限制为 Decodable。这意味着一旦它被声明,它将适用于所有符合 Decodable 的类型。没有为 Book 生成的单独实现。它们都是相同的实现,只是 T 不同。您应该关注的签名是:

func decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T : Decodable

它是如何实现的? source code 表示:

public func decode<T: Decodable>(
    _ type: T.Type,
    forKey key: Key
) throws -> T {
    return try _box.decode(T.self, forKey: key)
}

所以一个问题,比如“如果必须的话,你会如何实施[这个方法]?”意义不大,因为方法已经实现了,不用自己实现了。

很明显,该实现对于理解其工作原理不是很有用。它只是将调用委托给其他东西。该调用最终被委托给 KeyedDecodingContainerProtocol,即 KeyedDecodingContainerProtocol 的哪个实现取决于您使用的解码器。例如,JSONDecoder 将使用与 PropertyListDecoder 不同的解码容器。

现在问题来了,KeyedDecodingContainerProtocol中的decode方法是如何实现的?好吧,正如我们已经确定的那样,这取决于解码器。 JSON 解码器会做一些 JSON 特定的事情,而 属性 列表解码器会做一些 属性 列表特定的事情,但最终他们可能会调用 T.init(from:) 获取 T 的实例。 KeyedDecodingContainerProtocol.decode 你可以自己实现的东西。如果您正在编写自己的解码器,则需要实现它。 T.init(from:) 如果您希望以自定义方式解码可编码对象,您也应该实现。

我找不到 JSONDecoder 的源代码,所以这里有一个开源 XML 解码器,您可以探索:https://github.com/ShawnMoore/XMLParsing

This is how they implemented KeyedDecodingContainerProtocol.decode. This calls unbox, which calls T.init(from:) here.