如果可以,使用 Codable
进行 JSON 解析会非常方便。
有时需要理解未知的 JSON 结构。对于这些情况,需要使用 JSONSerialization
并尝试向下转型为各种类型。不幸的是,这会使用 Any
类型,它无法通过类型系统提供关于预期结构的指导。
TypedJSON 旨在添加一些结构,并通过使用 @dynamicMemberLookup
提供一些便利。一些灵感来自 SE-0195 中定义的 JSON
。
引入了两个核心类型:JSON.Container
和 JSON.Value
。它们的简化定义如下:
enum JSON {
enum Container: Equatable {
case Array([Value])
case Dictionary([String: Value])
}
enum Value: Equatable {
case Container(Container)
case String(String)
case Number(NSNumber)
case Bool(Bool)
case Null
}
}
这允许使用标准的 Swift 模式匹配来解包 case。这两种类型都支持通过动态成员查找、字符串索引和整数索引进行下标访问。
动态成员查找和字符串下标访问仅对于 JSON.Container
的 .Dictionary
case,以及 JSON.Value
的 .Container
case(当容器为 .Dictionary
case 时)才有意义。整数索引仅对于 JSON.Container
的 .Array
case,以及 JSON.Value
的 .Container
case(当容器为 .Array
case 时)才有意义。
如果索引(动态成员、字符串或整数)不存在,下标将返回 nil
。如果下标查找没有“意义”(如上定义),也会发生相同的情况。这种行为允许在无需显式模式匹配每个 case 的情况下遍历结构。
这个库在编码时的主要目标是准确地表示有效的 JSON 数据,并且能够以代表输入的方式与解码后的 JSON 数据进行交互。这个目标将指导一些决策。
只能对 JSON.Container
进行编码,因此只有这种类型才有 encoded
方法。
func encoded(options: JSONSerialization.WritingOptions = []) -> Data
options
接受与传递给 JSONSerialization.data(withJSONObject:options:)
相同的选项。
请记住,因为只能编码 JSON.Container
,所以 .fragmentsAllowed
选项没有意义。
可以使用 JSON.decode(_:)
方法或直接调用初始化器 JSON.Container(encoded:)
来解码 JSON。
目前无法提供额外的解码选项。
可以解码和读取 JSON,如下所示:
let jsonData = """
{"foo":{"bar":{"baz":"boo"}},"baz":[2]}
""".data(using: .utf8)!
let decoded = try JSON.decode(jsonData)
// traverse structure, only extract if string
guard case .String(let baz) = decoded.foo?.bar?.baz else {
return
}
print(baz) // "boo"
可以使用 Swift 字面量编写 JSON,以下代码将生成与上述相同的结构:
let json: JSON.Contianer = ["foo":["bar":["baz":"boo"]],"baz":[2]]
// traverse structure, only extract if string
guard case .String(let baz) = json.foo?.bar?.baz else {
return
}
print(baz) // "boo"