/ˈdʒeɪsən/ JAY-sən
JSEN(JSON Swift 枚举表示法)是一个轻量级的 JSON 枚举表示,使用 Swift 编写。
JSON,正如 ECMA-404 标准 中定义的那样,可以是
因此,JSON 可以表示为递归枚举(或者 Swift 中的 indirect enum
),从而有效地在 Swift 中创建静态类型的 JSON 负载。
这是无处不在的代码库中用于表示 JSON 的臭名昭著的 [String:Any]
的类型安全版本。如果你热爱 Swift,这应该已经足够成为一个理由了 😉 如果还不够,请继续阅读以发现这个简单枚举中存在的所有语法糖!
使用 Swift Package Manager
dependencies: [
.package(name: "JSEN", url: "https://github.com/rogerluan/JSEN", .upToNextMajor(from: "1.0.0")),
]
我认为为了理解它的简单性,可视化 JSEN 声明至关重要
/// A simple JSON value representation using enum cases.
public enum JSEN : Equatable {
/// An integer value.
case int(Int)
/// A floating point value.
case double(Double)
/// A string value.
case string(String)
/// A boolean value.
case bool(Bool)
/// An array value in which all elements are also JSEN values.
indirect case array([JSEN])
/// An object value, also known as dictionary, hash, and map.
/// All values of this object are also JSEN values.
indirect case dictionary([String:JSEN])
/// A null value.
case null
}
就是这样。
现在你已经熟悉了 JSEN,它提供了一些语法糖实用程序,例如符合大多数 ExpressibleBy…Literal
协议
ExpressibleByIntegerLiteral
初始化器返回一个 .int(…)
。ExpressibleByFloatLiteral
初始化器返回一个 .double(…)
。ExpressibleByStringLiteral
初始化器返回一个 .string(…)
。ExpressibleByBooleanLiteral
初始化器返回一个 .bool(…)
。ExpressibleByArrayLiteral
初始化器返回一个 .array(…)
,只要其 Elements 是 JSEN。ExpressibleByDictionaryLiteral
初始化器返回一个 .dictionary(…)
,只要其键是字符串且值是 JSEN。ExpressibleByNilLiteral
初始化器返回一个 .null
。当你想要构建像这样的 JSON 结构时,符合 ExpressibleBy…Literal
协议非常棒
let request: [String:JSEN] = [
"key": "value",
"another_key": 42,
]
但是如果你不使用字面量呢?
let request: [String:JSEN] = [
"amount": normalizedAmount // This won't compile
]
引入…
let request: [String:JSEN] = [
"amount": %normalizedAmount // This works!
]
自定义的 %
前缀运算符将任何 Int
、Double
、String
、Bool
、[JSEN]
和 [String:JSEN]
值转换为其各自的 JSEN 值。
根据设计,没有添加将 Optional
转换为 .null
的支持,以防止误用。
为了说明围绕 %optionalValue
操作可能出现的问题,请想象以下场景
let request: [String:JSEN] = [
"middle_name": %optionalString
]
network.post(path: "user", parameters: request)
network.put(path: "user", parameters: request)
network.patch(path: "user", parameters: request)
network.mergePatch(path: "user", parameters: request)
在上述场景中,你认为 RESTful 预期的行为应该是什么?
如果 %
运算符检测到非空的 String,那很好。但是,如果它检测到其底层值为 .none
(即 nil
),它会将该值转换为 .null
,当编码时,它将被转换为 NSNull()
(更多信息请参见下面的 Codable 部分)。正如你所想象的那样,在 RESTful API 方面,NSNull()
和 nil
的行为非常不同——前者可能会删除数据库上的键信息,而后者将被 Swift Dictionary 简单地忽略(就好像该字段根本不存在一样)。
因此,如果你想使用可选值,请显式调用,要么使用 .null
如果你知道该值必须编码为 NSNull()
实例,要么解包其值并将其包装在非空的 JSEN case 之一中。
当然!我们不能错过这个。JSEN 原生支持 Encodable & Decodable
(即 Codable
),因此你可以轻松地将 JSEN 解析为/从 JSON 类似结构。每种 case 都映射到其各自的值类型,而 .null
映射到 NSNull()
实例(在 JSON 中,它由 null
表示)。
还添加了一个额外的实用程序,即 decode(as:)
函数。它接收一个符合 Decodable 协议的 Type 作为参数,并将尝试使用两步策略将 JSEN 值解码为给定的类型
Data
,并尝试将该 Data
解码为给定的类型。.string(…)
case,它会尝试使用 .utf8
编码 JSEN 的字符串。如果能够编码,它会尝试将生成的 Data
解码为给定的类型。最后但并非最不重要的是,是 KeyPath
下标。
基于 @olebegemann 的 文章,KeyPath
是一个简单的结构体,用于表示字符串的多个段。它可以通过字符串字面量(例如 "this.is.a.keypath"
)初始化,并且在初始化时,字符串会按句点分隔,从而构成结构体的段。
JSEN 的下标允许以下语法
let request: [String:JSEN] = [
"1st": [
"2nd": [
"3rd": "Hello!"
]
]
]
print(request[keyPath: "1st.2nd.3rd"]) // "Hello!"
没有这种语法,你将不得不创建多个笨拙的可选链,并以奇怪且冗长的方式解包它们,才能访问字典中的嵌套值。我不是那样做的粉丝 :)
如果你发现任何错误、遗漏,或者你想对本项目提出改进建议,请提交 Issue 或 Pull Request 并附上你的想法,我保证在 24 小时内回复你!😇
JSEN 主要基于 Swift 中的静态类型 JSON 负载 以及 Stack Overflow 和 Swift 论坛中传播的此实用程序的其他各种实现。我将我需要的一切都集中在这个项目中,因为我找不到类似的 Swift Package,它拥有我需要的一切。
本项目是开源的,并受标准的 2 条款 BSD 许可证保护。这意味着你可以使用(公开、商业和私有)、修改和分发本项目的内容,只要你提及 Roger Oba 作为此代码的原始作者,并在你的应用程序、存储库、项目或研究论文中复制 LICENSE 文本。
忘记 “v2.1.3 版本是什么时候上线的?” 和 “应用准备好测试了吗?”
Statused 监控 App Store Connect 并在 Slack 上直接向你发送通知。
了解更多:statused.com
Twitter: @rogerluan_