PMJSON 将不会在这个仓库中继续维护。请使用、创建 issue 并向 位于此处的 PMJSON fork 版本 提交 PR。
PMJSON 提供了一个纯 Swift 强类型 JSON 编码器/解码器,以及一套方便的方法,用于转换为/从 Foundation 对象转换,以及解码 JSON 结构。
整个 JSON 编码器/解码器可以在不依赖 Foundation 框架的情况下使用,只需从项目中移除 ObjectiveC.swift
和 DecimalNumber.swift
文件即可。项目其余部分唯一的依赖是 Darwin
,用于 strtod()
和 strtoll()
。ObjectiveC.swift
文件添加了在 JSON
值和 Foundation 对象之间转换的便捷方法,以及从 Data
解码的功能,而 DecimalNumber.swift
添加了将值转换为 NSDecimalNumber
的便捷访问器。
在深入细节之前,这里有一个为结构体编写解码器的简单示例。关于如何处理格式错误的数据(例如,是否忽略错误类型的值,以及是否尝试将非字符串值强制转换为字符串或反之亦然)有几种不同的选项,但以下示例将相当严格,并且会为类型不正确的值抛出错误
struct Address {
var streetLine1: String
var streetLine2: String?
var city: String
var state: String?
var postalCode: String
var country: String?
init(json: JSON) throws {
streetLine1 = try json.getString("street_line1")
streetLine2 = try json.getStringOrNil("street_line2")
city = try json.getString("city")
state = try json.getStringOrNil("state")
postalCode = try json.toString("postal_code") // coerce numbers to strings
country = try json.getStringOrNil("country")
}
}
这是一个解码嵌套值数组的示例
struct Person {
var firstName: String
var lastName: String? // some people don't have last names
var age: Int
var addresses: [Address]
init(json: JSON) throws {
firstName = try json.getString("firstName")
lastName = try json.getStringOrNil("lastName")
age = try json.getInt("age")
addresses = try json.mapArray("addresses", Address.init(json:))
}
}
如果您不想处理错误,只想处理可选值,您也可以这样做
struct Config {
var name: String?
var doThatThing: Bool
var maxRetries: Int
init(json: JSON) {
name = json["name"]?.string
doThatThing = json["doThatThing"]?.bool ?? false
maxRetries = json["maxRetries"]?.int ?? 10
}
}
这个库还提供了对 Swift.Encoder
和 Swift.Decoder
的支持。详情请参阅 此部分。
JSON 解码器被分为独立的解析器和解码器阶段。解析器消耗任何 Unicode 标量序列,并生成 JSON “事件” 序列(类似于 SAX XML 解析器)。解码器接受 JSON 事件序列并生成一个 JSON
值。这种架构的设计使得您可以仅使用解析器,以便直接解码到您自己的数据结构,并在需要时完全绕过 JSON
表示形式。然而,大多数客户端预计会同时使用这两个组件,这通过一个简单的方法 JSON.decode(_:options:)
公开。
将 JSON 字符串解析为 JSON
值非常简单,只需
let json = try JSON.decode(jsonString)
JSON 解析器中的任何错误都表示为 JSONParserError
值,并从 decode()
方法抛出。错误包含错误的精确行号和列号,以及描述问题的代码。
还提供了一个便捷方法,用于从包含编码为 UTF-8、UTF-16 或 UTF-32 的 Data
中解码
let json = try JSON.decode(data)
编码 JSON
值也很简单
let jsonString = JSON.encodeAsString(json)
您还可以直接编码到任何 TextOutputStream
JSON.encode(json, toStream: &output)
同样,也提供了一个便捷方法来处理 Data
let data = JSON.encodeAsData(json)
PMJSON 支持解析 JSON 流,JSON 流是多个顶级 JSON 值,带有可选的空白分隔符(例如 {"a": 1}{"a": 2}
)。使用此功能的最简单方法是使用 JSON.decodeStream(_:)
,它返回一个 JSONStreamValue
延迟序列,其中包含 JSON
值或 JSONParserError
错误。您也可以直接使用 JSONParser
和 JSONDecoder
,以便更精细地控制流处理。
如上所述,JSON 解码器被分为独立的解析器和解码器阶段。JSONParser
是解析器阶段,它封装了任何 UnicodeScalar
序列,并且本身也是 JSONEvent
的序列。JSONEvent
是 JSON 解析的单个步骤,例如当遇到 {
时的 .objectStart
,或者当遇到 "string"
时的 .stringValue(_)
。如果您想对 JSON 进行任何类型的延迟处理(例如,如果您正在处理一个巨大的 JSON blob,并且不想一次性将整个内容解码到内存中),您可以直接使用 JSONParser
来发出事件流。
类似地,JSONDecoder
是解码器阶段。它封装了 JSONEvent
的序列,并将该序列解码为正确的 JSON
值。被封装的序列还必须符合一个单独的协议 JSONEventIterator
,该协议提供行/列信息,这些信息在发出错误时使用。如果您想封装除 JSONParser
之外的事件序列,或者如果您想要与 JSONStreamDecoder
提供的 JSON 流解码不同的接口,您可以直接使用 JSONDecoder
。
由于这种分离的性质,您可以轻松地提供您自己的事件流或您自己的解码阶段。或者您可以执行诸如将 JSONParser
包装在适配器中之类的操作,该适配器在将事件传递给解码器之前修改事件(这可能比转换生成的 JSON
值更有效)。
除了编码/解码之外,此库还提供了一套全面的访问器,用于从 JSON
值中获取数据。提供了 4 种基本类型的访问器
.string
。如果值与类型匹配,这些访问器返回底层值,如果值类型不正确,则返回 nil
。例如,.string
返回 String?
。这些访问器不在类型之间转换,例如 JSON.Int64(42).string
返回 nil
。as
开头的属性访问器,例如 .asString
。这些访问器也返回一个可选值,但如果这样做有意义,它们会在类型之间进行转换。例如,JSON.Int64(42).asString
返回 "42"
。get
开头的方法,例如 getString()
。这些方法返回非可选值,如果值的类型不匹配,则抛出 JSONError
。这些方法不在类型之间转换,例如 try JSON.Int64(42).getString()
会抛出一个错误。对于每种类型的方法,还有一个以 OrNil
结尾的变体,例如 getStringOrNil()
,它确实返回一个可选值。这些方法仅在值为 null
时返回 nil
,否则它们会抛出一个错误。to
开头的方法,例如 toString()
。这些方法与 get
方法类似,只是它们在适当时在类型之间进行转换,使用与 as
方法相同的规则,例如 try JSON.Int64(42).toString()
返回 "42"
。与 get
方法一样,也有以 OrNil
结尾的变体。JSON
还提供了键控和索引下标运算符,它们返回 JSON?
,并且始终可以安全调用(即使索引超出范围)。它还提供了 2 种下标访问器
get
访问器,都有一个变体,它接受键或索引。这些变体等效于对接收器进行下标运算,并在结果上调用 get
访问器,只是它们产生更好的错误(并且它们正确处理丢失的键/超出范围的索引)。例如,getString("key")
或 getString(index)
。如果键不存在或索引超出范围,OrNil
变体也会返回 nil
。to
访问器也有下标等效项。最后,getObject()
和 getArray()
访问器提供了接受闭包的变体。建议使用这些变体而不是基本访问器,因为它们产生更好的错误。例如,给定以下 JSON
{
"object": {
"elements": [
{
"name": null
}
]
}
}
以及以下代码
try json.getObject("object").getArray("elements").getObject(0).getString("name")
此代码抛出的错误将具有描述 "name: expected string, found null"
。
但给定以下等效代码
try json.getObject("object", { try $0.getArray("elements", { try $0.getObject(0, { try $0.getString("name") }) }) })
此代码抛出的错误将具有描述 "object.elements[0].name: expected string, found null"
。
所有这些访问器在 JSONObject
类型(表示对象的类型)上也都可用。
上面的最后一个代码片段看起来非常冗长,但在实践中,您最终不会编写这样的代码。相反,您通常最终只会编写类似这样的代码
try json.mapArray("elements", Element.init(json:))
JSON
类型具有静态方法 map()
、flatMap()
和 compactMap()
,用于处理数组(因为 PMJSON 没有定义自己的数组类型)。与使用等效的 SequenceType
方法相比,使用这些 PMJSON 静态方法的好处是它们产生更好的错误。
还有一些助手函数用于转换为/从 Foundation 对象转换。JSON
提供了一个初始化器 init(ns: Any) throws
,它可以将任何 JSON 兼容的对象转换为 JSON
。JSON
和 JSONObject
都提供了属性 .ns
,它返回一个等效于 JSON
的 Foundation 对象,以及 .nsNoNull
,它执行相同的操作,但省略任何 null
值,而不是使用 NSNull
。
JSON
类型符合 Codable
协议,因此它可以编码为 Swift.Encoder
,并从 Swift.Decoder
解码。这已经针对标准库提供的 JSONEncoder
和 JSONDecoder
进行了测试。由于解码协议的限制,解码 JSON
必须尝试解码多种不同类型的值,因此,编写不佳的 Swift.Decoder
在解码 JSON
时可能会产生令人惊讶的结果。
编码到 JSON.Encoder
和从 JSON.Decoder
解码已优化,以避免不必要的工作。
这个库提供了一个名为 JSON.Encoder
的 Swift.Encoder
实现。它可以将任何 Encodable
类型编码为 JSON
、String
或 Data
。它的使用方式类似于 Swift.JSONEncoder
(只是目前它没有选项来控制特定类型的编码)。
这个库提供了一个名为 JSON.Decoder
的 Swift.Decoder
实现。它可以从 JSON
、String
或 Data
解码任何 Decodable
类型。它的使用方式类似于 Swift.JSONDecoder
(只是目前它没有选项来控制特定类型的解码)。
测试套件包含一些基本的性能测试。使用 PMJSON 解码约 70KiB 的 JSON 所需时间大约是 NSJSONSerialization
的 2.5-3 倍,尽管我没有用不同的输入分布进行测试,并且这种性能可能特定于测试输入的特性。但是,使用 PMJSON 将相同的 JSON 编码回 Data
实际上更快,大约只需要 NSJSONSerialization
所需时间的 75%。这些基准测试是在 Swift 2.x 中执行的,并且自那时以来数字可能已经发生了变化。
作为框架安装需要最低 iOS 8、OS X 10.9、watchOS 2.0 或 tvOS 9.0。
使用任何机制安装后,您可以通过将 import PMJSON
添加到您的代码来使用它。
可以使用 Swift Package Manager 安装 PMJSON,方法是将其添加到您的 dependencies
列表中
let package = Package(
name: "YourPackage",
dependencies: [
.package(url: "https://github.com/postmates/PMJSON.git", from: "3.0.1")
]
)
要使用 Carthage 安装,请将以下内容添加到您的 Cartfile 中
github "postmates/PMJSON" ~> 3.0
此版本支持 Swift 4。如果您想要 Swift 3.x 支持,可以使用
github "postmates/PMJSON" ~> 2.0
要使用 CocoaPods 安装,请将以下内容添加到您的 Podfile 中
pod 'PMJSON', '~> 3.0'
此版本支持 Swift 4。如果您想要 Swift 3.x 支持,可以使用
pod 'PMJSON', '~> 2.0'
在以下任一许可证下授权
除非您明确声明另有说明,否则您为纳入作品而有意提交的任何贡献应获得上述双重许可,且不附加任何额外条款或条件。
JSON.Encoder
和 JSON.Decoder
编码/解码 URL
时,编码和解码它们的绝对字符串,而不是依赖于将它们编码为对象的原生实现。这与 JSONEncoder
和 JSONDecoder
的行为一致。JSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSeconds
的可用性属性。JSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSeconds
和 JSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSeconds
的最低支持版本提升至 iOS 11.2+ 和 tvOS 11.2+,因为尽管常量被标记为更早可用,但在运行时并不支持。(#33)JSONObject.ns
和 JSONObject.nsNoNull
转换为返回 [String: Any]
而不是 [AnyHashable: Any]
。(#25)JSON.Encoder.encodeAs*
和 JSON.Decoder.decode
方法拆分为重载对,其中一对接受 options:
,另一对不接受。这使得更容易将对 JSONEncoder
/JSONDecoder
方法的函数引用替换为 PMJSON 中的等效方法。TopLevelEncoder
和 TopLevelDecoder
的一致性,使用 Data
作为输入/输出类型。这意味着 JSON.Encoder.encode(_:)
现在被标记为已弃用而不是不可用。JSON.flatMap*
和 JSONObject.flatMap*
方法重命名为 .compactMap*
。(#28)@inlinable
。JSONError.withPrefix(_:)
,该方法通过在路径前添加前缀来返回新错误。如果在现有便捷函数无法满足您的需求时,可以在自定义解析代码中使用此方法来生成良好的错误信息。(#26).pretty
输出。JSON.encodeAsData()
。现在它几乎与 JSON.encodeAsString()
一样快。JSON.Encoder.encodeAsString()
和 JSON.Encoder.encodeAsData()
。JSON
添加几个模仿枚举案例的便捷静态方法:JSON.int(_:)
和 JSON.cgFloat(_:)
。当 JSON(_:)
触发过多的类型复杂性时,可以使用这些方法。还为 CGFloat
添加了 JSON(_:)
重载。JSON.Encoder.keyEncodingStrategy
。这与 Swift 4.1 的 JSONEncoder.keyEncodingStrategy
非常相似,但默认情况下它不会应用于任何 JSON
或 JSONObject
类型的嵌套值(还有另一个选项 applyKeyEncodingStrategyToJSONObject
控制此行为)。JSON.Decoder.keyDecodingStrategy
。这与 Swift 4.1 的 JSONDecoder.keyDecodingStrategy
非常相似,但默认情况下它不会应用于解码任何 JSON
或 JSONObject
类型的值(还有另一个选项 applyKeyDecodingStrategyToJSONObject
控制此行为)。JSON.Encoder.dateEncodingStrategy
。这与 JSONEncoder.dateEncodingStrategy
非常相似,只是它还包括另一种用于编码带有小数秒的 ISO8601 格式日期的用例(在 Apple 平台上)。JSON.Decoder.dateDecodingStrategy
。这与 JSONDecoder.dateDecodingStrategy
非常相似,只是它还包括另一种用于解码带有小数秒的 ISO8601 格式日期的用例(在 Apple 平台上)。JSON.Encoder.dataEncodingStrategy
。这与 JSONEncoder.dataEncodingStrategy
完全相同。JSON.Decoder.dataDecodingStrategy
。这与 JSONDecoder.dataDecodingStrategy
完全相同。JSONError.path
。JSONError.withPrefixedCodingPath(_:)
,以便在 Decodable
实现中更轻松地使用 JSONError
抛出方法。JSON
上实现 Codable
。JSON.Decoder
的 Swift.Decoder
实现。JSON.Encoder
的 Swift.Encoder
实现。Decimal
添加支持(在 Swift 3.1 及更高版本上)。注意:Decimal 支持在 Swift 3.1 中仍然存在错误,并且我们在 Apple 平台上用于获取正确值的解决方法在 Linux 上不起作用。在 Swift 修复其 Decimal 实现之前,您可能不应该依赖它在 Linux 上正常工作。Data
编码/解码添加支持。LocalizedError
添加 Linux 支持(仅真正适用于 Swift 3.1 及更高版本)。swift test
运行测试套件。JSON.parser(for:options:)
,该方法从 Data
返回 JSONParser<AnySequence<UnicodeScalar>>
。与 JSON.decode(_:options:)
类似,此方法自动检测 UTF-8、UTF-16 或 UTF-32 输入。JSON
变体 .decimal
、任何相关的访问器以及使用新选项 .useDecimals
的完全解析/解码支持的形式。使用此选项,任何原本会被解码为 Double
的数字都将改为解码为 Decimal
。forEach
访问器,类似于现有的 map
和 flatMap
访问器。OptionSet
)。采用 strict/pretty 标志的旧方法现在标记为已弃用。1e-1
或 1e+1
形式的数字的不正确解析。strict
选项时,停止接受 01
或 -01
形式的数字。Data
时对 UTF-16 的支持。Data
时存在 UTF-8 BOM,则跳过它。Hashable
添加到 JSONEvent
和 JSONParserError
。JSONParserError
符合 CustomNSError
,以便获得更好的 Obj-C 错误信息。JSONParser
和 JSONDecoder
现在都可以在流模式下运行,添加了一个新的类型 JSONStreamDecoder
作为 JSON 值的延迟序列,并添加了一个便捷方法 JSON.decodeStream(_:)
。JSONEventGenerator
重命名为 JSONEventIterator
,将 JSONParserGenerator
重命名为 JSONParserIterator
。旧名称仍然可用(但已弃用),以实现向后兼容性。JSONParserError
进行模式匹配的支持。它现在应该像任何其他错误一样工作,允许您说例如 if case JSONParserError.invalidSyntax = error { … }
。json["foo"].object?["key"] = "bar"
的代码。NSError
的错误提供本地化描述。JSONParser
添加对 JSON 值流(例如 "[1][2]"
)的支持。JSON
和 JSONObject
上添加一组便捷方法,用于映射通过使用键或索引进行下标运算返回的数组:mapArray(_:_:)
、mapArrayOrNil(_:_:)
、flatMapArray(_:_:)
和 flatMapArrayOrNil(_:_:)
。JSON
初始化器集合。JSON
和 JSONObject
的 description
和 debugDescription
以使其更有用。description
现在是 JSON 编码的字符串。JSON
和 JSONObject
实现 CustomReflectable
。plist
的实例重命名为 ns
。旧名称仍然可用,但标记为已弃用。初始版本发布。