PMJSON 4.0.0

PMJSON 4.0.0

测试已测试
Lang语言 SwiftSwift
许可 NOASSERTION
发布最新版本2019年11月
SPM支持 SPM

Lily Ballard 维护。



PMJSON 4.0.0

PMJSON

Version Platforms Languages License Carthage compatible CocoaPods

PMJSON 提供了一个纯 Swift 强类型 JSON 编码/解码器,以及一系列便捷方法来实现到/从 Foundation 对象的转换以及解码 JSON 结构。

整个 JSON 编码/解码器可以在没有 Foundation 的情况下使用,只需从项目中删除 ObjectiveC.swiftDecimalNumber.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.EncoderSwift.Decoder 的支持。有关详细信息,请参阅 本节

解析

JSON解码器分为单独的解析和解码阶段。解析器消费任何Unicode标量序列,并产生一个类似SAX XML解析器的JSON "事件"。解码器接受一个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)

JSON流

PMJSON支持解析JSON流,多个顶层JSON值可以有可选空白分隔符(例如{"a": 1}{"a": 2})。使用此功能的最简单方法是使用JSON.decodeStream(_:),它返回一个包含JSON值或JSONParserError错误的惰性序列的JSONStreamValue。您还可以直接使用《JSONParser》和《JSONDecoder》来获得对流的更精细控制。

JSONParserJSONDecoder

如上所述,JSON解码器分为解析和解码两个阶段。JSONParser是解析阶段,它封装任何Unicode标量序列,自身是JSONEvent序列。一个JSONEvent是一个JSON解析的步骤,如遇到{时的.objectStart,或者在遇到"string"时的.stringValue(_)。如果您想进行任何类型的懒处理(例如,如果您在处理单个巨大的JSON数据块,并且不想一次性将整个内容解码到内存中),可以直接使用JSONParser来发射事件流。

同样地,JSONDecoder 是解码阶段。它封装一系列 JSONEvent,将这些事件解码成适当的 JSON 值。封装的序列也必须符合另一个独立的协议 JSONEventIterator,该协议提供了行/列信息,用于错误输出。如果您想为除 JSONParser 之外的事件序列封装,或者想要一个不同于 JSONStreamDecoder 提供的 JSON 流解码接口,您可以直接使用 JSONDecoder

由于其拆分性质,您可以选择提供自己的事件流或解码阶段。或者,您可以对 JSONParser 进行适配,以修改在传递给解码器之前的事件(这样做可能比将生成的 JSON 值转换为其他类型更有效)。

获取器

除了编码/解码外,这个库还提供了一套获取 JSON 价值观的获取器。提供了4种基本类型:

  1. 基本属性获取器,命名方式例如 .string。这些获取器在其值为适当类型时返回底层值,如果值不是正确的类型,则返回 nil。例如,.string 返回 String?。这些获取器不进行类型之间的转换,例如 JSON.Int64(42).string 返回 nil
  2. as 开头的属性获取器,如 .asString。这些获取器也返回一个可选值,但如果合适,它们会进行类型之间的转换。例如,JSON.Int64(42).asString 返回 "42"
  3. get 开头的方法,如 getString()。这些方法返回非可选值,并在值的类型不匹配时抛出 JSONError。这些方法不进行类型之间的转换,例如尝试 JSON.Int64(42).getString() 会抛出错误。对于此类方法,还有一个以 OrNil 结尾的变体,例如 getStringOrNil(),它也会返回一个可选值。这些方法只在值是 null 时返回 nil,否则抛出错误。
  4. to 开头的方法,如 toString()。这些方法与 get 方法类似,但在适当的情况下将类型进行转换,使用与 as 方法相同的规则,例如尝试 JSON.Int64(42).toString() 返回 "42"。像 get 方法一样,也有以 OrNil 结尾的变体。

JSON 还提供了键和索引的获取器,它们总是安全的调用(即使没有边界索引也是如此)。

  1. 对于每个基本 get 获取器,都有一个变体接受一个键或索引。这些类似于对接收者进行索引访问然后再调用获取器,除了它们会产生更好的错误(并且妥善处理缺少的键/超出范围的索引)。例如,getString("键")getString(index)。具有 OrNil 变体的获取器如果键不存在或索引超出范围,则返回 nil
  2. 类似地,还有用于 to 获取器的索引等效。

最后,getObject()getArray() 获取器提供接受闭包的变体。建议使用这些变体代替基本获取器,因为它们会产生更好的错误。例如,给定以下 JSON

{
  "object": {
    "elements": [
      {
        "name": null
      }
    ]
  }
}

以及以下代码

try json.getObject("object").getArray("elements").getObject(0).getString("name")

此代码引发的错误将描述为"名称:期望字符串,找到null"

但有以下等效代码:

try json.getObject("object", { try $0.getArray("elements", { try $0.getObject(0, { try $0.getString("name") }) }) })

此代码引发的错误将描述为"object.elements[0].name:期望字符串,找到null"

所有这些访问器都在JSONObject类型上可用(它是表示对象的类型)。

上面的最后一小段代码看起来非常冗长,但在实践中,你不会写出这样的代码。相反,你通常会只写下类似以下内容

try json.mapArray("elements", Element.init(json:))

Helps

JSON类型有静态方法map()flatMap()compactMap(),用于处理数组(因为PMJSON没有定义自己的数组类型)。使用这些方法而不是使用等效的SequenceType方法的优点是PMJSON静态方法能产生更好的错误。

还有转换为/从Foundation对象的帮助器。JSON提供了一个初始化器init(ns: Any) throws,它可以将任何兼容JSON的对象转换为JSONJSONJSONObject都提供了一个属性.ns,它返回与JSON相当的Foundation对象,以及.nsNoNull,它执行同样的任务,但省略任何null值而不是使用NSNull

支持Codable

JSON类型符合Codable协议,因此它可以编码为Swift.Encoder并从Swift.Decoder解码。这已经在标准库提供的JSONEncoderJSONDecoder方面进行了测试。由于解码协议的限制,解码JSON必须尝试解析多种不同类型的值,因此如果Swift.Decoder编写得不好,解码JSON可能会产生意外结果。

将数据编码到JSON.Encoder并从JSON.Decoder解码已优化以避免不必要的操作。

Swift.EncoderSwift.Decoder

此库提供了一个名为JSON.EncoderSwift.Encoder实现。它可以编码任何EncodableJSONStringData。其使用方法与Swift.JSONEncoder类似(但截至目前,它没有选项来控制特定类型的编码)。

此库提供了一个名为JSON.DecoderSwift.Decoder实现。它可以解码任何DecodableJSONStringData。其使用方法与Swift.JSONDecoder类似(但截至目前,它没有选项来控制特定类型的解码)。

性能

测试套件包含一些基本的性能测试。使用 PMJSON 解码大约 70KiB 的 JSON 大约需要 NSJSONSerialization 所需时间的 2.5-3 倍,但未使用不同输入分布进行测试,因此这种性能可能是特定于测试输入特征的。然而,将相同的 JSON 编码回 Data 实际上更高效,大约是 NSJSONSerialization 所需时间的 75%。这些基准测试是在 Swift 2.x 上进行的,因此自那时以来数值可能已发生变化。

要求

作为框架安装,至少需要 iOS 8,OS X 10.9,watchOS 2.0 或 tvOS 9.0。

安装

安装完成后,可以通过在代码中添加 import PMJSON 使用它。

Swift 包管理器

可以使用 Swift 包管理器 通过将其添加到您的 dependencies 列表来安装 PMJSON。

let package = Package(
    name: "YourPackage",
    dependencies: [
        .package(url: "https://github.com/postmates/PMJSON.git", from: "3.0.1")
    ]
)

Carthage

要使用 Carthage 安装,请将以下内容添加到您的 Cartfile 中

github "postmates/PMJSON" ~> 3.0

此版本支持 Swift 4。如果您需要 Swift 3.x 支持,则可以使用

github "postmates/PMJSON" ~> 2.0

CocoaPods

使用 CocoaPods 进行安装,请在 Podfile 中添加以下内容

pod 'PMJSON', '~> 3.0'

此版本支持 Swift 4。如果您需要 Swift 3.x 支持,则可以使用

pod 'PMJSON', '~> 2.0'

许可证

此许可证归以下之一

贡献

除非您明确指出,否则您提交的任何旨在包含在工作中的贡献将按照上述双重许可,不附加任何附加条款或条件。

版本历史

v4.0.0 (2019-11-14)

  • 更新到 Swift 5。
  • 当使用 JSON.EncoderJSON.DecoderURL 进行编码/解码时,请对它们的绝对字符串进行编码和解码,而不是依赖于将它们作为对象进行编码的本地实现。这与 JSONEncoderJSONDecoder 的行为相匹配。
  • 修正了对 JSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSeconds 的可用性属性。
  • JSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSecondsJSON.Encoder.DateEncodingStrategy.iso8601WithFractionalSeconds 提升至 iOS 11.2+ 和 tvOS 11.2+,尽管该常量被标记为可用,但在运行时不支持。(#33
  • JSONObject.nsJSONObject.nsNoNull 的返回值从 [AnyHashable: Any] 转换为 [String: Any]。 (#25)
  • JSON.Encoder.encodeAs*JSON.Decoder.decode 方法拆分为带 options: 和不带 options: 的重载对。这使得替换 JSONEncoder/JSONDecoder 方法引用为 PMJSON 中的等效方法变得更容易。
  • 添加满足 Combine 的 TopLevelEncoderTopLevelDecoder 的兼容,使用 Data 作为输入/输出类型。这意味着现在 JSON.Encoder.encode(_:) 被标记为弃用,而不是不可用。
  • 当变换返回可选值时,将 JSON.flatMap*JSONObject.flatMap* 方法重命名为 .compactMap*。(#28
  • 将许多方法标记为 @inlinable

v3.1.2 (2018-11-06)

  • 添加方法 JSONError.withPrefix(_:),它通过将前缀添加到路径来返回新的错误。这可以在自定义解析代码中使用,以产生更好的错误,如果现有的便利函数不能满足需求。(#26

v3.1.1 (2018-05-17)

  • 消除 Swift 4.1 警告。

v3.1.0 (2018-02-25)

  • 改进空数组/字典的 .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,尽管默认情况下它不会应用于类型为 JSONJSONObject 的嵌套值(存在另一个选项 applyKeyEncodingStrategyToJSONObject 来控制这一点)。
  • 添加 JSON.Decoder.keyDecodingStrategy。这与Swift 4.1的JSONDecoder.keyDecodingStrategy非常相似,但默认情况下,它不会应用于解码类型为JSONJSONObject的任何值(还有一个选项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完全相同。

v3.0.2 (2018-02-21)

  • 添加便利属性 JSONError.path
  • 添加方法 JSONError.withPrefixedCodingPath(_:),以使在Decodable实现中使用抛出JSONError的实例方法变得更容易。

v3.0.1 (2018-02-18)

  • 修复Swift包管理器支持。

v3.0.0 (2018-02-18)

  • 转换为Swift 4。
  • JSON上实现Codable
  • 添加名为JSON.DecoderSwift.Decoder实现。
  • 添加名为JSON.EncoderSwift.Encoder实现。

v2.0.3 (2017-09-12)

  • Decimal添加Linux支持(在Swift 3.1及更高版本上)。注意:Swift 3.1中的Decimal支持仍然存在漏洞,我们在Apple平台上获得正确值时所使用的工作方法在Linux上不起作用。可能不应依赖Linux上的正确性,除非Swift修复了其Decimal实现。
  • 为解码和编码到/从Data添加Linux支持。
  • 在错误类型上添加Linux支持LocalizedError(主要适用于Swift 3.1及更高版本)。
  • 修复使用发布配置编译的过程。
  • 支持使用swift test运行测试套件。

v2.0.2 (2017-03-06)

  • 修复Linux兼容性问题。

v2.0.1 (2017-02-26)

  • 添加从Data返回JSONParser<AnySequence<UnicodeScalar>>的方法JSON.parser(for:options:)。与JSON.decode(_:options:)类似,此方法可以自动检测UTF-8、UTF-16或UTF-32输入。
  • 修复与Swift包管理器的兼容性问题。

v2.0.0 (2017-01-02)

  • 在支持平台上实现十进制数字的全面支持。这包括一个新的JSON变体.decimal、任何相关访问器和带有新选项.useDecimals的完整解析/解码支持。使用此选项,任何原本会解码为Double的数字将解码为Decimal
  • 为工作与数组相关的工作添加了一个forEach访问器集,类似于现有的mapflatMap访问器。

v1.2.1 (2016-10-27)

  • 处理UTF-32输入。
  • 无BOM时检测UTF-16和UTF-32输入。
  • 修复了一个错误,当我们处理UTF-16输入时没有传递解码器选项。

v1.2.0 (2016-10-27)

  • 更改向编码器/解码器/解析器提供选项的方式。现在,所有选项都以数组字面量语法的形式提供(类似于OptionSet)。旧方法现在已被标记为已弃用。
  • 为解码器添加了一个新的深度限制选项,默认值为10,000。
  • 根据JSONTestSuite实现一个新测试套件。
  • 修复输入流中包含没有前导代理的重尾代理导致崩溃的问题。
  • 修复解析形式为1e-11e+1的数字不正确的问题。
  • 当指定strict选项时,停止接受形式为01-01的数字。
  • 在解码具有UTF-16 BOM的Data时添加对UTF-16的支持。
  • 在解码Data时跳过UTF-8 BOM(如果有)。

v1.1.0 (2016-10-20)

  • Hashable添加到JSONEventJSONParserError中。
  • 使JSONParserError符合CustomNSError,以便更好地处理Obj-C错误。
  • 全面支持JSON流。现在JSONParserJSONDecoder都可以以流模式操作,新增了一个新类型JSONStreamDecoder,它是一个JSON值的延迟序列,并且添加了一个便利方法JSON.decodeStream(_:)
  • JSONEventGenerator重命名为JSONEventIterator,将JSONParserGenerator重命名为JSONParserIterator。旧名称可供使用(但已弃用),以保持向后兼容性。
  • JSONParserError添加模式匹配支持。现在它应该像任何其他错误一样工作,允许您说例如if case JSONParserError.invalidSyntax = error { … }

v1.0.1 (2016-09-15)

  • 修复CocoaPods。

v1.0.0 (2016-09-08)

  • 支持Swift 3.0。
  • 添加基本访问器的setter,以便您可以编写如下代码:json["foo"].object?["key"] = "bar"
  • 在桥接到NSError时为错误提供本地化描述。
  • JSONParser添加对JSON值流的支持(例如"[1][2]")。

v0.9.3 (2016-05-23)

  • JSONJSONObject上添加一组便利方法,以便于通过键或索引访问返回的数组:`mapArray(_:_:)`,`mapArrayOrNil(_:_:)`,`flatMapArray(_:_:)`和`flatMapArrayOrNil(_:_:)`。
  • 添加一组新的便利 JSON 初始化器。
  • 修改 descriptiondebugDescriptionJSONJSONObject 以使其更加有用。description 现在是 JSON 编码字符串。
  • JSONJSONObject 实现 CustomReflectable

v0.9.2 (2016-03-04)

  • CocoaPods 支持。

v0.9.1 (2016-02-19)

  • Linux 支持。
  • Swift Package Manager 支持。
  • 将 API 中 plist 的实例重命名为 ns。旧名称仍然可用,但已标记为已弃用。
  • 支持最新的 Swift 快照(2012-02-08)。

v0.9 (2016-02-12)

初版发布。