【问题标题】:Swift Decoding a Codable optional heterogeneous collection快速解码 Codable 可选异构集合
【发布时间】:2020-04-08 11:23:36
【问题描述】:

使用本教程https://medium.com/@kewindannerfjordremeczki/swift-4-0-decodable-heterogeneous-collections-ecc0e6b468cf 我正在尝试解码异构集合。这适用于非可选集合,但是我需要扩展此方法以使用可选集合。

我已经添加了我认为我需要能够执行此操作的方法,但是我在一行中遇到了我不知道如何修复的错误。

/// To support a new class family, create an enum that conforms to this protocol and contains the different types.
protocol ClassFamily: Decodable {
    /// The discriminator key.
    static var discriminator: Discriminator { get }

    /// Returns the class type of the object coresponding to the value.
    func getType() -> AnyObject.Type
}

/// Discriminator key enum used to retrieve discriminator fields in JSON payloads.
enum Discriminator: String, CodingKey {
    case type = "type"
}

extension JSONDecoder {
    /// Decode a heterogeneous list of objects.
    /// - Parameters:
    ///     - family: The ClassFamily enum type to decode with.
    ///     - data: The data to decode.
    /// - Returns: The list of decoded objects.
    func decode<T: ClassFamily, U: Decodable>(family: T.Type, from data: Data) throws -> [U] {
        return try self.decode([ClassWrapper<T, U>].self, from: data).compactMap { $0.object }
    }

    /// Decode a optional heterogeneous list of objects.
    /// - Parameters:
    ///     - family: The ClassFamily enum type to decode with.
    ///     - data: The data to decode.
    /// - Returns: The optional list of decoded objects.
    func decodeIfPresent<T: ClassFamily, U: Decodable>(family: T.Type, from data: Data) throws -> [U]? {
        return try self.decodeIfPresent(family: [ClassWrapper<T, U>].self, from: data)?.compactMap { $0.object }
    }

    private class ClassWrapper<T: ClassFamily, U: Decodable>: Decodable {
        /// The family enum containing the class information.
        let family: T
        /// The decoded object. Can be any subclass of U.
        let object: U?

        required init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: Discriminator.self)
            // Decode the family with the discriminator.
            family = try container.decode(T.self, forKey: T.discriminator)
            // Decode the object by initialising the corresponding type.
            if let type = family.getType() as? U.Type {
                object = try type.init(from: decoder)
            } else {
                object = nil
            }
        }
    }
}

extension KeyedDecodingContainer {

    /// Decode a heterogeneous list of objects for a given family.
    /// - Parameters:
    ///     - family: The ClassFamily enum for the type family.
    ///     - key: The CodingKey to look up the list in the current container.
    /// - Returns: The resulting list of heterogeneousType elements.
    func decode<T : Decodable, U : ClassFamily>(family: U.Type, forKey key: K) throws -> [T] {
        var container = try self.nestedUnkeyedContainer(forKey: key)
        var list = [T]()
        var tmpContainer = container
        while !container.isAtEnd {
            let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
            let family: U = try typeContainer.decode(U.self, forKey: U.discriminator)
            if let type = family.getType() as? T.Type {
                list.append(try tmpContainer.decode(type))
            }
        }
        return list
    }


    /// Optionally decode a heterogeneous list of objects for a given family.
    /// - Parameters:
    ///     - family: The ClassFamily enum for the type family.
    ///     - key: The CodingKey to look up the list in the current container.
    /// - Returns: The resulting list of heterogeneousType elements.
    func decodeIfPresent<T : Decodable, U : ClassFamily>(family: U.Type, forKey key: K) throws -> [T]? {
        var container = try self.nestedUnkeyedContainer(forKey: key)
        var list = [T]()
        var tmpContainer = container
        while !container.isAtEnd {
            let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
            let family: U? = try typeContainer.decodeIfPresent(U.self, forKey: U.discriminator)
            if let type = family?.getType() as? T.Type {
                list.append(try tmpContainer.decode(type))
            }
        }
        if list.isEmpty {
            return nil
        } else {
            return list
        }
    }
}

```JSONDecoder.decodeIfPresent()```中的这一行

return try self.decodeIfPresent(family: [ClassWrapper<T, U>].self, from: data)?.compactMap { $0.object }

以下问题的错误

Generic parameter 'U' could not be inferred
Instance method 'decodeIfPresent(family:from:)' requires that '[JSONDecoder.ClassWrapper<T, U>]' conform to 'ClassFamily'

任何关于如何让这些方法与可选集合一起工作的指针将不胜感激

【问题讨论】:

    标签: swift decodable jsondecoder


    【解决方案1】:

    问题是您试图从它自己的实现中递归调用decodeIfPresent(family:from:) 而没有提供基本案例。相反,您应该调用内置的decodeIfPresent 方法,但遗憾的是JSONDecoder 上不存在该方法,它只存在于其容器中。

    但是,您可以定义自己的 decodeIfPresent,只需捕获 DecodingError.keyNotFound.valueNotFound 并为它们返回 nil,否则让函数传播抛出的错误。

    func decodeIfPresent<T: ClassFamily, U: Decodable>(family: T.Type, from data: Data) throws -> [U]? {
        do {
            return try self.decode(family: family, from: data)
        } catch DecodingError.keyNotFound {
            return nil
        } catch DecodingError.valueNotFound {
            return nil
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 1970-01-01
      相关资源
      最近更新 更多