TypeDecoder 是一个 Swift 库,允许检查 Swift 语言的原生和复杂类型。 它最初是为了在 Kitura 项目中使用而编写的,但可以很容易地用于各种需要动态检查类型的项目。
TypeDecoder 的最新版本需要 Swift 4.0 或更高版本。 您可以通过此 链接 下载此版本的 Swift 二进制文件。 不保证与其他 Swift 版本的兼容性。
将 TypeDecoder
添加到应用程序的 Package.swift
文件中的依赖项中。 将 "x.x.x"
替换为最新的 TypeDecoder
版本。
.package(url: "https://github.com/Kitura/TypeDecoder.git", from: "x.x.x")
将 TypeDecoder
添加到目标依赖项
.target(name: "example", dependencies: ["TypeDecoder"]),
TypeDecoder.decode()
返回一个 TypeInfo
枚举,该枚举描述传递给 decode()
的类型。 TypeInfo
枚举值是
对于这些枚举值中的每一个,第一个参数始终是传递给 TypeDecoder
进行解码的原始类型。
要使用 TypeDecoder,您需要声明一个符合 Decodable
协议的类型,例如下面的 struct StructureType
。
import TypeDecoder
struct StructureType: Decodable {
let aString: String
}
然后,您调用 TypeDecoder.decode()
并传递要解码的类型。 如果您打印返回的 TypeInfo
枚举的内容,您可以看到它描述了传递给 decode()
的类型。
let structureTypeInfo = try TypeDecoder.decode(StructureType.self)
print("\(structureTypeInfo)")
打印 structureTypeInfo 将输出
StructureType{
aString: String
}
然后,您可以使用 .keyed(Any.Type, [String: TypeInfo])
来获取包含在键控结构中的所有字段的 Dictionary,这允许您按名称检索每个字段的 TypeInfo
。
在下面的示例中,字段 "aString" 的类型是一个基本类型,因此您可以使用 .single(Any.Type, Any.Type)
。 要调用的适当枚举值将根据字段类型而有所不同,请参阅下面的完整实现,了解如何解码更复杂的类型。
if case .keyed(_, let dict) = structureTypeInfo {
print("\nThe Dictionary returned from decoding StructureType contains:\n\(dict)")
/// Fields that are not containers will be .single
if let theTypeInfo = dict["aString"] {
if case .single(_) = theTypeInfo {
print("aString is type \(theTypeInfo)")
}
}
}
您将看到以下输出
The Dictionary returned from decoding StructureType contains:
OrderedDictionary<String, TypeInfo>(keys: ["aString"], values: ["aString": String], iteratorCount: 1)
aString is type String
/// Building and running this example shows how to decode a Swift data structure.
///
/// You should expect to see the following output:
///
/// Print the returned TypeInfo and you get this:
/// StructType{
/// myString: String,
/// myOptional: Float?,
/// myCyclic: [StructType{<cyclic>}],
/// myDict: [String:Bool],
/// myArray: [Int8]
/// }
///
/// The Dictionary returned from decoding StructType contains:
/// ["myString": String, "myOptional": Float?, "myCyclic": [StructType{<cyclic>}], "myDict": [String:Bool], "myArray": [Int8]]
///
/// Each field can be individually inspected:
/// myString is type String
/// myDict is type Dictionary<String, Bool>
/// myArray contains type Int8
/// myOptional is type Float
///
/// Cyclics are harder to deal with as they're buried inside an Array type:
/// myCyclic is type StructType
import TypeDecoder
struct StructType: Decodable {
let myString: String
let myDict: Dictionary<String, Bool>
let myArray: [Int8]
let myOptional: Float?
let myCyclic: [StructType]
}
func main() {
do {
let structTypeInfo = try TypeDecoder.decode(StructType.self)
/// Print the returned TypeInfo and you get this:
///
/// StructType{
/// myString: String,
/// myDict: [String:Bool],
/// myArray: [Int8],
/// myOptional: Float?,
/// myCyclic: [StructType{<cyclic>}]
/// }
print("Print the returned TypeInfo and you get this:\n\(structTypeInfo)")
if case .keyed(_, let dict) = structTypeInfo {
/// .keyed TypeInfo contains a Dictionary<String, TypeInfo> of all fields contained in
/// the keyed structure, so each field's TypeInfo can be retrieved by name.
print("\nThe Dictionary returned from decoding StructType contains:\n\(dict)")
print("\nEach field can be individually inspected:")
/// Fields that are not containers will be .single
if let theTypeInfo = dict["myString"] {
if case .single(_) = theTypeInfo {
print("myString is type \(theTypeInfo)")
}
}
/// .dynamicKeyed fields are Dictionary types
if let theTypeInfo = dict["myDict"] {
if case .dynamicKeyed(_, let keyTypeInfo, let valueTypeInfo) = theTypeInfo {
print("myDict is type Dictionary<\(keyTypeInfo), \(valueTypeInfo)>")
}
}
/// .unkeyed fields are Array or Set types
if let theTypeInfo = dict["myArray"] {
if case .unkeyed(_, let theTypeInfo) = theTypeInfo {
print("myArray contains type \(theTypeInfo)")
}
}
/// .optional field types
if let theTypeInfo = dict["myOptional"] {
if case .optional(let theTypeInfo) = theTypeInfo {
print("myOptional is type \(theTypeInfo)")
}
}
/// .cyclic fields are embedded inside .unkeyed (Array or Set) types
if let theTypeInfo = dict["myCyclic"] {
print("\nCyclics are harder to deal with as they're buried inside an Array type:")
if case .unkeyed(_, let theTypeInfo) = theTypeInfo {
if case .cyclic(let theTypeInfo) = theTypeInfo {
print("myCyclic is type \(theTypeInfo)")
}
}
}
}
} catch let error {
print(error)
}
}
main()
TypeDecoder
通过使用 Codable
框架来模拟类型的解码来工作。 为类型(以及任何嵌套类型)调用 init(from: Decoder)
初始化程序,以便发现其结构。 为了在没有序列化表示的情况下创建实例,解码器为每个字段提供虚拟值。
但是,在某些情况下,类型可能需要在初始化期间执行对这些值的验证。 TypeDecoder 提供了一种机制,用于通过 ValidSingleCodingValueProvider
和 ValidKeyedCodingValueProvider
协议在解码期间提供可接受的值。
下面是一个 enum
的示例,其原始值为 String
。 Swift 可以为此类类型合成 Codable 一致性,从而生成一个 init(from: Decoder)
,它需要一个与其中一个枚举用例匹配的有效字符串。 以下是如何扩展此类类型以使其与 TypeDecoder 兼容
public enum Fruit: String, Codable {
case apple, banana, orange, pear
}
// Provide an acceptable value during decoding
extension Fruit: ValidSingleCodingValueProvider {
public static func validCodingValue() -> Any? {
// Returns the string "apple"
return self.apple.rawValue
}
}
结构化类型的示例,其中验证其中一个字段,以及使其能够由 TypeDecoder 处理的扩展
public class YoungAdult: Codable {
let name: String
let age: Int
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: CodingKeys.name)
self.age = try container.decode(Int.self, forKey: CodingKeys.age)
// Validate the age field
guard self.age >= 18, self.age <= 30 else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Age is outside the permitted range"))
}
}
}
// Provide a value for 'age' which is within the acceptable range
extension YoungAdult: ValidKeyedCodingValueProvider {
public static func validCodingValue(forKey key: CodingKey) -> Any? {
switch key.stringValue {
case self.CodingKeys.age.stringValue:
return 20
default:
// For any fields that are not validated, you may return nil.
// The TypeDecoder will use a standard dummy value.
return nil
}
}
}
在 TypeDecoder 中实现的扩展 Foundation 类型的示例,该类型需要验证,并且使用数字 CodingKeys
extension URL: ValidKeyedCodingValueProvider {
public static func validCodingValue(forKey key: CodingKey) -> Any? {
switch key.intValue {
case 1?: return "http://example.com/"
default: return nil
}
}
}
有关更多信息,请访问我们的 API 参考。
我们喜欢讨论服务器端 Swift 和 Kitura。 加入我们的 Slack 来认识团队!
该库在 Apache 2.0 下获得许可。 完整的许可证文本可在 LICENSE 中找到。