一个最简单的使用SwiftyJSON的示例:
let a = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
SwiftyJSON
提供了多种初始化接口,根据不同类型的数据在内部进行不同的转换最终都归纳到统一的接口完成初始化:
fileprivate init(jsonObject: Any) {
self.object = jsonObject
}
但在此之前,多种多样或符合JSON标准或不符合的数据都要经过中转站:
public init(_ object: Any) {
switch object {
case let object as [JSON] where object.count > 0:
self.init(array: object)
case let object as [String: JSON] where object.count > 0:
self.init(dictionary: object)
case let object as Data:
self.init(data: object)
default:
self.init(jsonObject: object)
}
}
通过这个函数,SwiftyJSON
将数据转换为需要的Any
对象,例如,当我们将一个JSON
类型的数组对一个JSON
进行初始化(虽然我不知道在何种状况下能达成条件):
let a = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
let b = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
let c = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
let d = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
let e = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
let arr = [a,b,c,d,e]
let output = JSON.init(arr)
SwiftyJSON
会通过遍历JSON
数组以获取代表真实数据的JSON
实例对象的object属性,object属性根据数据真实类型获取对应类型的数据,最终返还给output
,生成新JSON
对象,这一步的作用是避免多层嵌套JSON的产生导致的结构趋向复杂。
[String: JSON]
也是同理。
最后,我们获得且仅获得一个可能包含任意数据类型但表现为数据安全的object
对象。
set {
_error = nil
switch newValue {
case let number as NSNumber:
if number.isBool {
_type = .bool
self.rawBool = number.boolValue
} else {
_type = .number
self.rawNumber = number
}
case let string as String:
_type = .string
self.rawString = string
case _ as NSNull:
_type = .null
case _ as [JSON]:
_type = .array
case nil:
_type = .null
case let array as [Any]:
_type = .array
self.rawArray = array
case let dictionary as [String : Any]:
_type = .dictionary
self.rawDictionary = dictionary
default:
_type = .unknown
_error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
}
}
当object
属性被赋值时,逐步判断object的类型,并赋值枚举,同时向相应的代表真实数据的属性填值。
于此,SwiftyJSON
完成了对数据的安全存储,保证即使数据错误也不会导致崩溃的后果,这很重要,因为每次通过json[0]["user"]["name"]
诸如这样的方式去获取被SwiftyJSON
存储的数据时都是在重复上述的初始化方式。
fileprivate subscript(index index: Int) -> JSON {
get {
if self.type != .array {
var r = JSON.null
r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"])
return r
} else if index >= 0 && index < self.rawArray.count {
return JSON(self.rawArray[index])
} else {
var r = JSON.null
r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"])
return r
}
}
set {
if self.type == .array {
if self.rawArray.count > index && newValue.error == nil {
self.rawArray[index] = newValue.object
}
}
}
}
SwiftyJSON
实现了复数的下标函数分别对数字下标、字符串下标、数字及字符串多参数下标以及数字与字符串数组下标等形式的数据获取与请求方式。
如上面的源码,当调用数字下标时,首先判断调用者在初始化时赋予的类型是否是数组,若是则取出对应的真实数据数组中的真实数据,并使用新的JSON
容器包裹返回,通过JSON
初始化函数避免崩溃性错误以及通过这种类链式语法方便操作。
字符串下标亦是同理。
有趣的地方在于数字下标与字符串下标混合使用,就像这样:
let name = json[1,"list",2,"name"].string
跟这样:
let name = json[[1,"list",2,"name"]].string
SwiftyJSON
声明了一个JSONSubscriptType
协议,协议仅包含一个名为JSONKey
的属性,该属性为一个枚举:
public enum JSONKey
{
case index(Int)
case key(String)
}
然后,通过扩展分别实现了Int
类型与String
类型的扩展,使其遵循协议:
extension Int: JSONSubscriptType {
public var jsonKey:JSONKey {
return JSONKey.index(self)
}
}
extension String: JSONSubscriptType {
public var jsonKey:JSONKey {
return JSONKey.key(self)
}
}
当let name = json[1,"list",2,"name"]
被调用时,其被作为一组参数传入下标函数,接着,首先忽略其过程,SwiftyJSON
需要的结果是根据下标数组的顺序拆解最开始的JSON
容器,每一个下标做出新的JSON
容器,然后从这个JSON
中取出真实数据,再用新的JSON
容器包裹,直至遍历完整个下标数组。
在获得数组后,在暂时不以SwiftyJSON
所写的代码为最优解时,我们其实有多种方式可以实现这个目的,比如将混合数组作为[Any]
进行遍历,根据元素类型进行强转再调用JSON
的下标函数,这样也可以达到相同的目的。
而SwiftJSON
则是以reduce
函数来应对:
//path: [JSONSubscriptType]
path.reduce(self) { $0[sub: $1] }
通过reduce
函数的性质,将JSON
容器自身作为初始值不断地根据下标获取新容器。
在sub
下标函数中通过JSONKey
再将Int
或String
下标发给对应的下标函数。
switch sub.jsonKey {
case .index(let index):
return self[index: index]
case .key(let key): return self[key: key]
}
另外,SwiftyJSON
也实现了循环字典及循环数组的功能。
for (key: String, subJson: JSON) in json {
//Do something you want
}
for (index: String, subJson: JSON) in json {
//Do something you want
}
通过声明Collection
协议并实现迭代器相关函数,根据初始化时赋予的type
属性判断容器中的真实数据是否为字典或者数组中的一种,调用其真实数据的迭代器函数。
public subscript (position: Index) -> (String, JSON)
{
switch position
{
case .array(let idx):
return (String(idx), JSON(self.rawArray[idx]))
case .dictionary(let idx):
let (key, value) = self.rawDictionary[idx]
return (key, JSON(value))
default:
return ("", JSON.null)
}
}
由此完成了SwiftyJSON
的绝大多数功能,剩下的七百多行代码都是对导出真实数据的扩展,例如将NSNumber
类型转化为Double
类型导出,布尔值类型导出成数字类型等等等等,不再一一叙述。