解析 JSON 优雅且安全可能很困难,但 Freddy 可以帮您解决这个问题。Freddy 是一个针对 Swift JSON 解析的可复用框架。它有三大主要优势。
首先,Freddy 为 Swift 中的 JSON 解析提供了一个类型安全的解决方案。这意味着编译器会帮助您发送和接收 JSON,以帮助防止运行时崩溃。
其次,Freddy 提供了一种利用 Swift 的泛型、枚举和函数式功能的惯用 JSON 解析解决方案。这一切都无需记住我们的文档来理解我们的魔法自定义操作符。Freddy 没有任何这些。如果您对用 Swift 编写代码(使用扩展、协议、初始化器等)感到舒适,那么您不仅可以理解 Freddy 的组织结构,而且还会感到舒适地使用 Freddy。
第三,Freddy 提供了在解析 JSON 时经常出现的错误的好信息。如果您使用不在的键对 JSON 对象进行索引,您将获得更有用的错误。如果您的索引超出范围,您将获得有用的错误。如果您尝试将 JSON 值转换为错误类型,您在这里也会得到一个很好的错误。
因此,Freddy 与 JSON 相比,谁赢得比赛?我们认为赢得比赛的是 Freddy。
本部分描述了 Freddy 的基本用法。您可以在 Wiki 中找到更多有关解析数据、处理错误、将=<>JSON
实例序列化为
NSData
的示例。您可以通过阅读文档 来查看完整的 API。
考虑一些示例 JSON 数据
{
"success": true,
"people": [
{
"name": "Matt Mathias",
"age": 32,
"spouse": true
},
{
"name": "Sergeant Pepper",
"age": 25,
"spouse": false
}
],
"jobs": [
"teacher",
"judge"
],
"states": {
"Georgia": [
30301,
30302,
30303
],
"Wisconsin": [
53000,
53001
]
}
}
这里是一个使用 Freddy 解析此数据的快速示例
let data = getSomeData()
do {
let json = try JSON(data: data)
let success = try json.bool("success")
// do something with `success`
} catch {
// do something with the error
}
在我们加载数据后,我们创建了一个 JSON
的实例,这是该框架的主力。这使我们能够访问 JSON 数据中的值。我们进行 try
因为数据可能损坏,并且解析可能会导致错误。接下来,我们通过调用 JSON
上的 bool(_:)
方法来访问 "success"
键。在这里我们同样进行 try
,因为访问 json
的 "success"
键可能会失败——例如,如果我们传递了一个未知的关键字。此方法接收两个参数,都用于定义要寻找感兴趣的真值布尔值的 JSON 实例中的路径。如果找到了路径描述的 "success"
中的 Bool
,则 bool(_:)
返回一个 Bool
。如果路径不导向一个 Bool
,则抛出适当的错误。
使用 Freddy,可以通过路径访问 json 结构中更深层的元素。例如
let data = getSomeData()
do {
let json = try JSON(data: data)
let georgiaZipCodes = try json.array("states","Georgia")
let firstPersonName = try json.string("people",0,"name")
} catch {
// do something with the error
}
在代码 json.array("states","Georgia")
中,键 "states"
和 "Georgia"
描述了 json
中乔治亚州邮编的路径。Freddy 术语称这个过程为“下标”JSON。位于,例如 array(_:)
的括号之间的类型是一个由逗号分隔的键和索引列表,用于描述感兴趣的值的路径。
可以有任意数量的下标,每个下标可以是表示 JSON 中一个命名元素的 String
,或者表示数组中一个元素的 Int
。如果路径中存在无效内容,如不存在于 JSON 中的索引,则会抛出错误。
现在,让我们看看一个将数据解析到模型类的例子
let data = getSomeData()
do {
let json = try JSON(data: data)
let people = try json.array("people").map(Person.init)
// do something with `people`
} catch {
// do something with the error
}
在这里,我们使用 array(_:)
方法将键 "people"
的值作为数组加载。该方法类似于上面看到的 bool(_:)
方法。它使用提供给方法的路径来查找一个数组。如果路径良好,该方法将返回一个 Array
的 JSON
。如果路径不好,则会抛出适当的错误。
然后我们可以在那个 JSON
数组上调用 map
。由于 Person
类型符合 JSONDecodable
,我们可以传入 Person
类型初始化器。这个调用将一个初始化器应用到数组的每个元素上,从而生成一个 Person
实例的数组。
这看起来像 JSONDecodable
public protocol JSONDecodable {
init(json: JSON) throws
}
这是一个相当简单的协议。它所需的所有就是,符合此协议的类型必须实现一个只接受一个 JSON
实例作为其唯一参数的初始化器。
要使所有这一切紧密相连,这给出了 Person
类型的外观
public struct Person {
public let name: String
public let age: Int
public let spouse: Bool
}
extension Person: JSONDecodable {
public init(json value: JSON) throws {
name = try value.string("name")
age = try value.int("age")
spouse = try value.bool("spouse")
}
}
Person
只是具有几个属性。它通过扩展符合 JSONDecodable
。在这个扩展中,我们实现了一个 throws
初始化器,它只接受一个 JSON
实例作为其唯一参数。在实现中,我们尝试了三个函数:1) string(_:)
,2) int(_:)
,和 3) bool(_:)
。这些工作方式就像您之前看到的那样。方法接收一个路径,该路径用于在传递给初始化器的 JSON
实例中查找特定类型的值。由于这些路径可能无效,或者请求的类型可能与 JSON
内的实际内容不匹配,因此这些方法可能会抛出错误。
Freddy的序列化支持以JSON.serialize()
方法为中心。
在JSON
枚举中,可以直接转换为NSData
。
let someJSON: JSON = …
let data: NSData = try someJSON.serialize()
虽然大部分对象都不是Freddy.JSON
对象,但您可以通过将它们首先转换为Freddy.JSON
,使用JSONEncodable.toJSON()
方法来实现,这是JSONEncodable
协议的唯一方法,然后再使用serialize()
方法将Freddy.JSON
转换为NSData
来对它们进行序列化。
let myObject: JSONEncodable = …
// Object -> JSON -> NSData:
let objectAsJSON: JSON = myObject.toJSON()
let data: NSData = try objectAsJSON.serialize()
// More concisely:
let dataOneLiner = try object.toJSON().serialize()
Freddy已提供常用Swift数据类型的定义。若要使自定义数据类型可序列化,则需将它符合到JSONEncodable
并实现该协议的toJSON()
方法。
extension Person: JSONEncodable {
public func toJSON() -> JSON {
return .Dictionary([
"name": .String(name),
"age": .Int(age),
"spouse": .Bool(spouse)])
}
}
Freddy需要iOS 7.0、Mac OS X 10.9、watchOS 2.0或tvOS 9.0。暂时不支持Linux。
iOS上的动态框架需要最低部署目标为iOS 8.0。对于目标为iOS 7的项目,请参阅“Submodules”。
您有几种不同的方法可以安装Freddy。
git submodule add https://github.com/bignerdranch/Freddy.git Vendor/Freddy
Freddy.xcodeproj
拖入您的Xcode项目。Freddy.framework
添加到您的应用目标的“链接框架和库”面板中。Cartage也可以用于检出依赖项以及维护Git子模块状态。
将它添加到您的Package.swift
中
import PackageDescription
let package = Package(
name: "My Nerdy App",
dependencies: [
.Package(url: "https://github.com/bignerdranch/Freddy.git", majorVersion: 2),
]
)
如果要用Freddy与iOS 7配合使用,则可能需要将Freddy的源代码文件复制到您的项目中。嵌入的框架在iOS 8+中才受支持。您可以将Freddy添加为子模块(见上文),然后确保将源文件添加到项目中。
在开始使用一套新的JSON时,为错误设置断点可能会有所帮助。这允许您在断点处探索JSON的结构。特别是,您很可能希望为Freddy
的JSON.Error
设置断点,以便您可以检查出了什么问题。
以下是如何设置此类断点的方法
1) 打开断点导航器
2) 点击左下角的“+”按钮
3) 选择“添加Swift错误断点”
现在,您拥有了一个只有在生成Swift错误时才会触发的断点。但您的程序会在任何任何Swift错误被抛出时中断运行。如果您只想在抛出Freddy
的JSON.Error
时中断,怎么办呢?
您可以对断点进行编辑,添加一个过滤器
1) 右键单击您的新错误断点
2) 选择“编辑断点…”
3) 将出现一个窗口,其中有一个“类型”文本框
4) 输入JSON.Error
基本上就是这样!现在,您拥有了一个只有当抛出类型为JSON.Error
的错误时才会触发的错误断点。可以查看框架的测试用例以获取更多使用示例。Wiki还有大量非常有用的信息。