一个 Swift 框架,用于读取、写入和解析 JSON 格式。
VJson 是 Swiftfire Web 服务器项目的一部分。
VJson 也被用作我们在 App Store 中的 proJSON 应用的核心。
VJson 可以通过 Swift Package Manager 使用。只需将其作为依赖项添加到您的包清单中即可。
或者,您可以克隆项目并以下列方式生成 Xcode 框架
$ git clone https://github.com/Balancingrock/VJson
$ cd VJson
$ swift package generate-xcodeproj
双击该项目以打开它。打开后,在创建框架之前,在 Build Settings -> Packaging
中将 Defines Module
设置为“yes”。(否则,在另一个项目中导入框架将不起作用)
在使用 VJson 的项目中,通过打开目标的 General
设置并将 VJson.framework 添加到 Embedded Binaries
来添加 VJson.framework。
在您想要使用它的 Swift 源代码中,在文件顶部导入 VJson。
此代码可以直接使用
func testExample() {
let top = VJson() // Create JSON hierarchy
top["books"][0]["title"] &= "THHGTTG"
let jsonCode = top.code
do {
let json = try VJson.parse(jsonCode)
if let title = (json|"books"|0|"title")?.stringValue {
XCTAssertEqual(title, "THHGTTG")
} else {
XCTFail("The title of the first book in the JSON code was not found")
}
} catch let error {
XCTFail("Parser failed: \(error)")
}
}
创建一个空的顶级 JSON 项目
let json = VJson()
从输入创建一个 JSON 层次结构
let json = try VJson.parse(fileUrl)
let json = try VJson.parse(uInt8Ptr, numberOfBytes)
let json = try VJson.parse(string)
let json = try VJson.parse(nsMutableData)
或没有异常
let json = VJson.parse(fileUrl) {... error handler ...}
let json = VJson.parse(uInt8Ptr, numberOfBytes) {... error handler ...}
let json = VJson.parse(string) {... error handler ...}
let json = VJson.parse(nsMutableData) {... error handler ...}
注意 1:从 NSMutableData 创建会从缓冲区中删除使用的数据。
注意 2:错误处理程序的签名是 (code: Int, incomplete: Bool, message: String) -> Void
。
创建值
let n = VJson(8)
let n = VJson(3.12)
let b = VJson(true)
let s = VJson("A String")
创建集合类型
let a = VJson.array()
let o = VJson.object()
创建一个命名项目
let n = VJson(8, name: "AnyName")
let n = VJson(3.12, name: "AnyName")
let b = VJson(true, name: "AnyName")
let s = VJson("A String", name: "AnyName")
let a = VJson.array(name: "AnyName")
let o = VJson.object(name: "AnyName")
创建 VJson 层次结构的首选方式
let json = VJson()
json["description"] &= "Book price list"
json["books"][0]["title"] &= "Book Title"
json["books"][0]["price"] &= 12.34
json["books"][1]["title"] &= "Second Book Title"
从层次结构检查/检索的首选方式
guard let title = (json|"books"|0|"title")?.stringValue else {...}
guard let price = (json|"books"|0|"price")?.doubleValue else {...}
可以使用 guard let title = json["books"][0]["title"].stringValue else {...}
,但这可能会产生副作用,可以通过使用管道运算符来避免。请参阅下面的“注意”部分。
将(命名)项目添加到对象
let o = VJson.object()
o.add(VJson(8), name: "AnyName") // {"AnyName":8}
等效于
let o = VJson.object()
o.add(VJson(8, name: "AnyName")) // {"AnyName":8}
将项目添加到数组
let a = VJson.array()
a.append(VJson(8)) // [8]
在层次结构中创建项目
let json = VJson() // {}
var i: Int?
json["first"].intValue = i // {"first":null}
或
let json = VJson() // {}
var i: Int?
i = 12
json["first"].intValue = i // {"first":12}
或使用定义的运算符“&=”
json["first"] &= 23 // {"first":23}
将“first”的类型更改为字符串
json["first"] &= "second" // {"first":"second"}
使用下标访问器隐式创建 JSON OBJECT 和 ARRAY
let json = VJson()
json["books"][2]["Authors"][0] &= "Jane" // {"books":[null, null, {"Authors":["Jane"]}]}
显式地将 JSON OBJECT 添加到 JSON OBJECT
let json = VJson() // {}
json["first"] &= "second" // {"first":"second"}
json.add(VJson.object(name: "Child")) // {"first":"second","Child":{}}
显式地将 JSON ARRAY 添加到 JSON OBJECT
json.add(VJson.array(), name: "Array") // {"first":"second","Child":{},"Array":[]}
显式地将 JSON OBJECT 添加到 JSON ARRAY
let json = VJson
json["Arr"].append(VJson.object(name: "No1")) // {"Arr":[{"No1":{}}]}
测试和检索值
guard let title = (json|"Book"|3|"Title")?.stringValue else { ... }
迭代对象(或数组)的所有子项
for child in json { ... }
以上示例都使用了“let”变量。如果使用“var”则有另一个选项可用于从 VJson 对象检索值
let json = try! VJson.parse(string: "{\"title\":\"A Good Read\"}")
var title: String?
title &= (json|"title")
下标访问器具有创建不存在的项目以满足完整路径的副作用。
即
let json = VJson()
guard let name = json["root"][2]["name"].stringValue else { return }
将创建解决该路径所需的所有项目。即使字符串值不会分配给 let 属性,因为它不存在。不必要的项目将在持久化层次结构之前自动删除。但是创建和销毁不必要的项目会花费可以以不同方式花费的时间。
要避免这些副作用,请使用管道运算符
let json = VJson()
guard let name = (json|"root"|2|"name")?.stringValue else { return }
作为一般规则:使用管道运算符从 JSON 层次结构中读取,并使用下标访问器创建 JSON 层次结构。
可以使用管道运算符将值分配给 JSON 层次结构,但前提是写入的项目已经存在
let json = VJson() // results in: {}
(json|"top"|2|"name")? &= 42 // results in: {}
json["top"][2]["name"] &= 42 // results in: {"top":[null, null, {"name":42}]}
VJson 层次结构中的每个对象都是一个 VJson 对象。 这非常方便,但同时也带来了一些挑战:JSON 规范根据值是否包含在 ARRAY 或 OBJECT 中以不同的方式处理值。 在 OBJECT 中,这些值具有名称。 在 ARRAY 中,值只是没有名称的值。 但是,由于 VJson 仅为所有内容提供一种类型的对象,因此该对象必须具有名称组件。 因此,此名称的可选性取决于 VJson 对象的使用位置。 如果将具有指定名称的 VJson VALUE 插入到数组中会怎样? 好吧,该名称将被忽略。 这(可能)会导致信息丢失,但另一种选择是在层次结构中创建一个额外的对象。 如果这是首选行为,则由应用程序显式创建该对象。 举个例子
let json = VJson()
json["top"].append(VJson(12)) // results in: {"top":[12]}
let json = VJson()
json["top"].append(VJson(12, name: "one")) // results in: {"top":[12]}
允许使用没有可选性的下标访问器需要对 JSON 项目类型转换采取宽松的态度。 为了防止应用程序代码无意中更改 JSON 项目的类型,如果发生这种情况,将会抛出致命错误。
JSON NULL 类型的类型更改始终被认为是合法的。
所有其他类型更改 - 例如 - BOOL 到 ARRAY 或 STRING 到 NUMBER,当静态选项 fatalErrorOnTypeConversion
设置为 'true' 时(这是默认设置),将导致致命错误。 只能通过显式地通过 JSON NULL 类型进行转换才能进行此类类型更改。
要允许对不涉及 NULL 的类型更改采取宽松的态度,请将 fatalErrorOnTypeConversion
设置为“false”。
但请注意,“add”和“append”操作并不宽松。 也就是说,不可能“add”到 ARRAY 或“append”到 OBJECT,因为这可能会导致数据丢失。 但是,存在将 ARRAY 更改为 OBJECT 以及反之亦然的操作。
Apple 解析器的优点是它更快,大约是 MacBook Pro 和 PowerMac 上标准解析器的两倍。 然而,Apple 的解析器至少有两个缺点
Apple 的解析器无法处理具有相同名称的多个 name/value 对。 也就是说,{"test":1,"test":2} 将失败。 使用提供的 VJson 解析器,这将导致一个对象,其中包含两个具有相同名称的子项。 可通过 "arrayValue" 访问器访问。
Apple 的解析器将 JSON BOOL 项作为 NSNumber 处理,因此必须将其作为 JSON NUMBER 项进行查询。 当包含 bool 值的 json 代码转换为 VJson 层次结构,然后该层次结构转换回代码时,这会导致对称性中断。 (输入和输出代码将不相同)
没有计划新功能。 根据需要临时进行更新以支持 Swiftfire 开发。