通用 JSON

Compatible from Swift 5.1 to 6. Compatible with macOS, iOS, visionOS, tvOS and watchOS. Compatible with Linux, Windows, WASI and Android.

通用 JSON 可以轻松处理自由格式的 JSON 字符串,而无需创建单独的、类型良好的结构。

Codable 和自由格式 JSON

Swift 4 引入了一个新的 JSON 编码和解码机制,由 Codable 协议表示。 这项功能非常棒且类型安全,这意味着不再可以随意解码 JSON 字符串并从中提取随机的、无类型的数据。 这在大多数情况下是好事™,但是当您确实想要随意编码或解码 JSON 字符串而不为其引入单独的、类型良好的结构时,您应该怎么做?

例如

/* error:
 *  heterogeneous collection literal could only be inferred to '[String : Any]';
 *  add explicit type annotation if this is intentional. */
let json = [
   "foo": "foo",
   "bar": 1,
]

/* Okay then: */
let json: [String: Any] = [
   "foo": "foo",
   "bar": 1,
]

/* But: fatal error: Dictionary<String, Any> does not conform to Encodable because Any does not conform to Encodable. */
let encoded = try JSONEncoder().encode(json)

所以这不能很好地工作。 此外,json 值无法与另一个值进行相等性检查,尽管任意 JSON 值应该支持相等性。

引入 JSON

用法

创建一个 JSON 结构

let json: JSON = [
   "foo": "foo",
   "bar": 1,
]

/* #"{"bar":1,"foo":"foo"}"# */
let str = try String(data: try JSONEncoder().encode(json), encoding: .utf8)!
let hopefullyTrue = (json == json) /* true! */

Encodable 对象转换为通用 JSON 结构

struct Player: Codable {
   let name: String
   let swings: Bool
}

let val = try JSON(encodable: Player(name: "Miles", swings: true))
val == [
   "name": "Miles",
   "swings": true,
] /* true */

查询值

考虑以下 JSON 结构

let json: JSON = [
   "num": 1,
   "str": "baz",
   "bool": true,
   "obj": [
      "foo": "jar",
      "bar": 1,
   ]
]

可以使用可选属性访问器、下标或动态成员下标来查询值

/* Property accessors. */
if let str = json.objectValue?["str"]?.stringValue {  }
if let foo = json.objectValue?["obj"]?.objectValue?["foo"]?.stringValue {  }

/* Subscripting. */
if let str = json["str"]?.stringValue {  }
if let foo = json["obj"]?["foo"]?.stringValue {  }

/* Dynamic member subscripting. */
if let str = json.str?.stringValue {  }
if let foo = json.obj?.foo?.stringValue {  }

您甚至可以使用点分隔的键路径来钻取嵌套结构

let val = json[keyPath: "obj.foo"] /* "jar" */