Codability
用于在Swift中处理Codable
类型的有用辅助工具
安装
Swift包管理器
将以下添加到您的Package.swift
依赖关系中
.package(url: "https://github.com/yonaskolb/Codability.git", from: "0.2.0"),
然后在需要的位置导入:import Codability
辅助工具
无效元素策略
默认情况下,如果数组或字典中的单个元素失败,Decodable将抛出错误。InvalidElementStrategy
是一个枚举,它允许您控制此行为。它有多个情况
remove
:从集合中移除元素fail
:将使整个解码失败。这是解码器使用的默认行为fallback(value)
:允许您以类型安全的方式提供带类型的值custom((EncodingError)-> InvalidElementStrategy)
:让您提供基于抛出错误的具体动态行为,使您能够查找哪些键参与了错误
在解码数组或字典时,使用decodeArray
和decodeDictionary
函数(还有IfPresent
变异体)。
InvalidElementStrategy
可以通过这些函数传递,或者可以通过JSONDecoder().userInfo[.invalidElementStrategy]
设置默认值,否则将使用默认的fail
。
给定以下JSON
{
"array": [1, "two", 3],
"dictionary": {
"one": 1,
"two": "two",
"three": 3
}
}
struct Object: Decodable {
let array: [Int]
let dictionary: [String: Int]
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RawCodingKey.self)
array = try container.decodeArray([Int].self, forKey: "array", invalidElementStrategy: .fallback(0))
dictionary = try container.decodeDictionary([String: Int].self, forKey: "dictionary", invalidElementStrategy: .remove)
}
}
let decoder = JSONDecoder()
// this will provide a default if none is passed into the decode functions
decoder.userInfo[.invalidElementStrategy] = InvalidElementStrategy<Any>.remove
let decodedObject = try decoder.decode(Object.self, from: json)
decodedObject.array == [1,0,3]
decodedObject.dictionary = ["one": 1, "three": 3]
任何Codable
使用Codable的缺点是,您不能编码和解码类型混合或未知的属性,例如[String: Any]
、[Any]
或Any
。这些在许多API中有时是必要的邪恶,而AnyCodable
使支持这些类型变得容易。
有两种不同的方法可以使用它
作为Codable属性
这种方法的优点是您可以使用合成的Codable函数。然而,缺点是这些值必须使用AnyCodable.value
解除包装。您可以通过为您的对象添加自定义的设置器和获取器来使其更容易访问。
struct AnyContainer: Codable {
let dictionary: [String: AnyCodable]
let array: [AnyCodable]
let value: AnyCodable
}
自定义解码和编码函数
这允许您保留正常的结构,但需要使用decodeAny
或encodeAny
函数。如果您必须出于其他原因实现自定义的init(from:)
或encode
函数,则此方法可行。在幕后,这使用AnyCodable
进行编码,然后在解码的情况下将其转换为您期望的类型。
struct AnyContainer: Codable {
let dictionary: [String: Any]
let array: [Any]
let value: Any
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dictionary = try container.decodeAny(.dictionary)
array = try container.decodeAny([Any].self, forKey: .array)
value = try container.decodeAny(Any.self, forKey: .value)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeAny(dictionary, forKey: .dictionary)
try container.encodeAny(array, forKey: .array)
try container.encodeAny(value, forKey: .value)
}
enum CodingKeys: CodingKey {
case dictionary
case value
case array
}
}
原始CodingKey
RawCodingKey
可以用来提供动态编码键。此外,当您只需要在一个地方使用这些值时,它还无需创建标准 CodingKey
枚举。
struct Object: Decodable {
let int: Int
let bool: Bool
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RawCodingKey.self)
int = try container.decode(Int.self, forKey: "int")
bool = try container.decode(Bool.self, forKey: "bool")
}
}
通用解码函数
在 KeyedDecodingContainer
和 UnkeyedDecodingContainer
上的默认解码函数都需要传递一个显式类型。 Codabilty
通过添加泛型函数来消除这种需求,使得您的 init(from:)
更加清晰。同时,key
参数也变为匿名。
由 Codabality
提供的所有辅助函数,如 decodeAny
、decodeArray
或 decodeDictionary
函数,也包括这些泛型变体,包括 IfPresent
。
struct Object: Decodable {
let int: Int?
let bool: Bool
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RawCodingKey.self)
// old
int = try container.decodeIfPresent(Int.self, forKey: "int")
bool = try container.decode(Bool.self, forKey: "bool")
// new
int = try container.decodeIfPresent("int")
bool = try container.decode("bool")
}
}
贡献
感谢 @mattt 和 Flight-School/AnyCodable 为 AnyCodable
的基础提供支持。许可