优雅且安全地解析 JSON 可能很困难,但 Freddy 可以提供帮助。Freddy 是一个用于在 Swift 中解析 JSON 的可重用框架。它有三个主要优点。
首先,Freddy 为 Swift 中的 JSON 解析提供了类型安全的解决方案。这意味着编译器可以帮助您以一种有助于防止运行时崩溃的方式处理 JSON 的发送和接收。
其次,Freddy 为 JSON 解析提供了一种符合 Swift 语言习惯的解决方案,该方案利用了 Swift 的泛型、枚举和函数式特性。这一切的提供都没有让您痛苦地记住我们的文档来理解我们神奇的自定义运算符。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.getBool(at: "success")
// do something with `success`
} catch {
// do something with the error
}
在我们加载数据后,我们创建了一个 JSON
实例,它是这个框架的核心。这使我们能够访问 JSON 数据中的值。我们使用 try
是因为 data
可能格式错误,解析可能会产生错误。接下来,我们通过调用 JSON
上的 getBool(at:)
方法来访问 "success"
键。我们在这里也使用 try
,因为访问键 "success"
的 json
可能会失败 - 例如,如果我们传递了一个未知的键。此方法接受两个参数,这两个参数都用于定义进入 JSON
实例的路径,以查找感兴趣的布尔值。如果在 "success"
描述的路径中找到 Bool
,则 getBool(at:)
返回 Bool
。如果路径没有指向 Bool
,则会抛出相应的错误。
使用 Freddy,可以使用路径来访问 JSON 结构中更深层的元素。例如
let data = getSomeData()
do {
let json = try JSON(data: data)
let georgiaZipCodes = try json.getArray(at: "states","Georgia")
let firstPersonName = try json.getString(at: "people",0,"name")
} catch {
// do something with the error
}
在代码 json.getArray(at: "states","Georgia")
中,键 "states"
和 "Georgia"
描述了 json
中佐治亚州邮政编码的路径。Freddy
的术语称此过程为“下标” JSON。例如,在 getArray(at:)
的括号之间键入的是逗号分隔的键和索引列表,它们描述了通往感兴趣值的路径。
可以有任意数量的下标,每个下标可以是 String
,表示 JSON 中的命名元素,也可以是 Int
,表示数组中的元素。如果路径中存在无效内容,例如 JSON 中不存在的索引,则会抛出错误。
现在,让我们看一个将数据解析为模型类的示例
let data = getSomeData()
do {
let json = try JSON(data: data)
let people = try json.getArray(at: "people").map(Person.init)
// do something with `people`
} catch {
// do something with the error
}
在这里,我们改为使用 getArray(at:)
方法从键 "people"
加载值作为数组。此方法的工作方式与您在上面看到的 getBool(at:)
方法非常相似。它使用提供给方法的路径来查找数组。如果路径正确,该方法将返回 JSON
的 Array
。如果路径错误,则会抛出相应的错误。
然后我们可以在该 JSON
数组上调用 map
。由于 Person
类型符合 JSONDecodable
,我们可以传入 Person
类型的初始化器。此调用将一个接受 JSON
实例的初始化器应用于数组中的每个元素,从而生成 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.getString(at: "name")
age = try value.getInt(at: "age")
spouse = try value.getBool(at: "spouse")
}
}
Person
只有几个属性。它通过扩展符合 JSONDecodable
。在扩展中,我们实现了一个 throws
初始化器,它以 JSON
实例作为其唯一参数。在实现中,我们 try
三个函数:1) getString(at:)
、2) getInt(at:)
和 3) getBool(at:)
。这些方法中的每一个都像您之前看到的那样工作。这些方法接收一个路径,该路径用于在传递给初始化器的 JSON
实例中查找特定类型的值。由于这些路径可能无效,或者请求的类型可能与 JSON
内部的实际类型不匹配,因此这些方法可能会抛出错误。
Freddy 的序列化支持围绕 JSON.serialize()
方法展开。
JSON
枚举直接支持转换为 Data
let someJSON: JSON = …
do {
let data: Data = try someJSON.serialize()
} catch {
// Handle error
}
虽然您的大多数对象都不是 Freddy.JSON
对象。但是,您可以将它们序列化为 Data
,方法是首先通过 JSONEncodable.toJSON()
(JSONEncodable
协议的唯一方法)将它们转换为 Freddy.JSON
,然后使用 serialize()
将 Freddy.JSON
转换为 Data
。
let myObject: JSONEncodable = …
// Object -> JSON -> Data:
let objectAsJSON: JSON = myObject.toJSON()
let data: Data = 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 8.0、Mac OS X 10.9、watchOS 2.0 或 tvOS 9.0。尚不支持 Linux。
您有几种不同的选项来安装 Freddy。
将我们添加到您的 Cartfile
github "bignerdranch/Freddy" ~> 3.0
运行 carthage bootstrap
后,将 Freddy.framework
添加到应用程序目标的“Linked Frameworks and Libraries”面板中。阅读更多。
将我们添加到您的 Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Freddy'
然后运行 pod install
。
git submodule add https://github.com/bignerdranch/Freddy.git Vendor/Freddy
Freddy.xcodeproj
拖到您的 Xcode 项目中。Freddy.framework
添加到应用程序目标的“Linked Frameworks and Libraries”面板中。Carthage 也可用于检出依赖项和维护 Git 子模块状态。
将我们添加到您的 Package.swift
import PackageDescription
let package = Package(
name: "My Nerdy App",
dependencies: [
.Package(url: "https://github.com/bignerdranch/Freddy.git", majorVersion: 3),
]
)
如果您想在 iOS 7 中使用 Freddy,则需要使用 Freddy 的早期版本。
当您开始使用新的 JSON 集时,为错误设置断点会很有帮助。这使您可以在中断时探索 JSON 的结构。特别是,您可能需要为 Freddy
的 JSON.Error
设置断点,以便您可以检查哪里出了问题。
以下是如何设置这种断点
转到断点导航器
单击左下角的“+”按钮
现在您有了一个断点,它只会在生成 Swift 错误时触发。但是,每当抛出*任何* Swift 错误时,您的程序都会中断。如果您只想为 Freddy
的 JSON.Error
错误中断怎么办?
您可以编辑断点以添加过滤器
右键单击您的新错误断点
选择“编辑断点...”
将出现一个窗口,其中包含一个用于“类型”的文本框
输入 JSON.Error
这就差不多完成了!您现在有了一个错误断点,它只会在抛出 JSON.Error
类型的错误时触发。请查看框架的测试以获取更多用法示例。Wiki 也包含许多非常有用的信息。