测试已测试 | ✓ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布最新发布 | 2016年10月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✗ |
由 Sean G. Young 维护。
一个旨在提供将 Swift 类型自动转换为 JSON 及其从 JSON 转换回的一种类型安全方法的库。
SGYSwiftJSON 是一个旨在大大简化仅 Swift 模型的序列化和反序列化的库。该库的主要目标是消除将任意对象转换为 JSON 以及相反操作所需的大部分代码。这包括对集合、字典和复杂数字中包含的类型的递归转换。它提供了现成的功能支持大多数常见的 Foundation 类型以及复杂类型的可继承的基础类(非必需,但更简便)。它还提供了一系列协议,允许扩展到非寻常对象。
大多数为 JSON 序列化编写的模型都已经得到了支持。任何符合以下条件的数据结构都应该可以即时工作
Array
、Set
、NSArray
或 NSMutableArray
类型,其元素类型遵循这些规则集合。String
或 NSString
键类型,其值类型遵循这些规则集合。JSONKeyValueCreatable
。这可以通过使用 JSONCreatableObject
作为基类来实现。NSNumber
、NSDecimalNumber
(可能被标记为可选)或者可以桥接到 NSNumber
(不能被标记为可选)。如果您不希望遵守上述限制,则可以使用定义的协议扩展大多数其他类型。有关这些协议的详细信息以及它们在序列化和反序列化过程中的评估方法,请见下文。
注意:如果您不想实现反序列化,则上述限制中的许多都不适用或者大大放宽。
序列化通过协议和 Swift 的 Mirror
支持。任何传递给进行序列化的对象都会检查以下条件
JSONProxyProvider
规范 - 对象中的 jsonProxy
属性会被检索并通过相同的逻辑树传递。JSONLeafRepresentable
规范 - 服从此协议的对象可以被表示为 JSON 叶子对象。例如,NSString
,NSNumber
或 NSNull
。除了接受的 3 个叶子值外,结构体 String
,Bool
,Int
,Float
和 Double
符合此规范。Date
类型 - 如果对象是 Date
类型且不符合上述任何协议,则使用 dateConversionBlock(如果提供)来将其转换为 JSONLeafValue
,或者如果没有提供块,则跳过属性。SGYDictionaryReflection
规范 - 对象将转换为键为字符串、值为对象的字典。通用的 Dictionary
和 NSDictionary
都遵循此规范。SGYCollectionReflection
规范 - 对象包含的元素将被转换并放入数组中。数组、NSArray
和 Set
都遵循此规范。Mirror
进行枚举并转换为字典。反序列化比序列化更困难,因为它要求所有类型都有无参初始化器,可以分配任意值,并且能够报告它们包含的类型。在反序列化 NSArray
或 NSDictionary
(JSONSerialization
产生的唯一对象)时,将执行以下逻辑
JSONKeyValueCreatable
协议,则使用 Mirror
确定对象的属性和类型值。由 JSONSerialization
产生的对象必须是一个 NSDictionary
,否则会抛出错误。JSONDictionaryCreatable
协议,则使用该协议的 keyValueTypes 属性检索对象的键和值类型。由 JSONSerialization
产生的对象必须是一个 NSDictionary
,否则会抛出错误。JSONCollectionCreatable
协议,则使用该协议的 elementType 属性检索对象的元素类型。由 JSONSerialization
产生的对象必须是一个 NSArray
,否则会抛出错误。Element
类型。类似地,字典的值将转换为它们的 Value
类型。对于复杂对象,值将使用其 Mirror
属性表示进行转换。此转换使用以下逻辑进行Any
、AnyObject
或声明类型与反序列化类型匹配,则直接分配反序列化类型。Date
,则使用 dateConversionBlock
将反序列化的 Any
值转换为 Date
。如果未声明块或块返回 nil,则不会分配属性。JSONLeafConvertable
并使用叶子值构造并分配。否则不会分配反序列化值。[Any]
类型且声明类型是 JSONCollectionCreatable
,则使用数组的转换逻辑初始化一个数组并返回。否则不会分配反序列化值。[String: Any]
类型且声明类型是 JSONDictionaryCreatable
,则使用字典的转换逻辑初始化一个数组并返回。否则不会分配反序列化值。所有示例都将基于一个任意的 Person
类。我们将从一个非常基本的类开始,并通过修改/扩展它来利用更复杂/Swifty 的属性。
让我们从一个无需转换的模型开始。
class Person {
var name: String?
var birthdate: String?
var favoriteColor: String?
var bestFriends: [Person]?
var categorizedFriends: [String: Person]?
var followersCount: NSNumber?
}
序列化很简单。假设 someGuy
是具有任意值的 Person
类的实例。
let serializer = SGYJSONSerializer()
do {
let jsonData = try serializer.serialize(someGuy)
} catch let error as NSError {
// Optionally catch specific errors
}
反序列化需要一个小改动。我们需要一种方法来分配类属性。最简单的方法是继承自实现了 JSONKeyValueCreatable
的 JSONCreatableObject
,它是通过 NSObject
的键值编码实现的。
class Person: JSONCreatableObject
然后进行反序列化。
let deserializer = SGYJSONDeserializer()
do {
let jsonGuy: Person = try deserializer.deserialize(jsonData)
} catch let error as NSError {
// Optionally catch specific errors
}
上述示例已经比正常转换要简多了。但我们仍在使用那个ObjC对象 NSNumber
,而展开它很烦人。幸运的是,对于序列化,Swift的自动桥接为我们完成了工作。我们可以简单地重新定义 followersCount
为:
var followersCount: Int?
这将顺利地进行序列化。但是反序列化是一个问题。属性 followersCount
将被正确地转换为 Int
。但是 JSONCreatableObject
使用键值编码,尝试分配给 Int?
会抛出错误。解决方案很简单
var followersCount: Int = 0 // No reason to be nil anyway. If value doesn't exist 0 is reasonable.
这将顺利地进行反序列化。如果你坚定地想使用可选的Foundation类型,请参阅下一节关于重写 setValue:forProperty
的信息。
现在模型看起来更接近于不考虑反序列化就能设计的东西。但是,关于 favoriteColor
属性呢?它绝对适用于成为Swift枚举。序列化同样简单。我们定义一个符合复合协议的 Color
枚举:
enum Color: String, JSONLeafEnum {
case red = "Red", green = "Green", blue = "Blue", yellow = "Yellow"
}
现在修改 Person
类型。
var favoriteColor: Color?
这将顺利地进行序列化并产生关联的 rawValue
作为JSON值。反序列化比 Int
更困难,原因相同。但是Swift枚举根本无法通过KVC分配。唯一的选项是重写 setValue:property:
override func setValue(value: Any, property: String) throws {
if property == "color" { color = value as? Color }
else { try super.setValue(value, property: property) }
}
现在 Person
类将把JSON值反序列化为可选的Swift枚举。
严格来说,日期不是JSON值。但是它的使用和转换需求是恒定的。因为有太多方式可以表示日期值,序列化和反序列化类都提供了一个用于转换的 dateConversionBlock
属性。首先,将 birthdate
转换为 Date
类型。
var birthdate: Date?
由于大多数日期的JSON表示都是字符串或数字类型,因此 SGYJSONSerializer
上的日期转换块预期返回一个 JSONLeafValue
枚举。假设我们的JSON消费者预期日期为 DateFormatter
的 MediumStyle
,那么与之前的序列化相比,唯一增加的是分配此块。
let formatter = DateFormatter()
formatter.dateStyle = .MediumStyle
serializer.dateConversionBlock = { (date) in formatter.stringFromDate(date) }
// Continue serialization as before
反序列化类似。主要区别在于反序列化器上的 dateConversionBlock
接受一个更通用的 Any
参数,以便将任意反序列化的值转换为 Date
。
let formatter = DateFormatter()
formatter.dateStyle = .mediumStyle
deserializer.dateConversionBlock = { (jsonValue) -> Date? in
guard let value = jsonValue as? String else { return nil }
return formatter.dateFromString(value)
}
// Continue deserialization as before