Swift 解码 Codable 可选异构集合
Swift Decoding a Codable optional heterogeneous collection
使用本教程 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 { [=11=].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 { [=11=].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 { [=12=].object }
以下问题的错误
Generic parameter 'U' could not be inferred
Instance method 'decodeIfPresent(family:from:)' requires that '[JSONDecoder.ClassWrapper<T, U>]' conform to 'ClassFamily'
任何关于如何让这些方法与可选集合一起工作的指示将不胜感激
问题是您试图从 decodeIfPresent(family:from:)
自己的实现中递归调用而不提供基本情况。相反,您应该调用内置的 decodeIfPresent
方法,但遗憾的是 JSONDecoder
上不存在该方法,它仅存在于其容器中。
然而,您可以通过简单地捕获 DecodingError.keyNotFound
和 .valueNotFound
并为它们返回 nil
来定义自己的 decodeIfPresent
,否则让函数传播抛出的错误。
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
}
}
使用本教程 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 { [=11=].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 { [=11=].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 { [=12=].object }
以下问题的错误
Generic parameter 'U' could not be inferred
Instance method 'decodeIfPresent(family:from:)' requires that '[JSONDecoder.ClassWrapper<T, U>]' conform to 'ClassFamily'
任何关于如何让这些方法与可选集合一起工作的指示将不胜感激
问题是您试图从 decodeIfPresent(family:from:)
自己的实现中递归调用而不提供基本情况。相反,您应该调用内置的 decodeIfPresent
方法,但遗憾的是 JSONDecoder
上不存在该方法,它仅存在于其容器中。
然而,您可以通过简单地捕获 DecodingError.keyNotFound
和 .valueNotFound
并为它们返回 nil
来定义自己的 decodeIfPresent
,否则让函数传播抛出的错误。
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
}
}