Skip to content

Commit 45cb776

Browse files
author
Sven
committed
fix: 测试用例优化
1 parent fbfbd8c commit 45cb776

File tree

4 files changed

+143
-22
lines changed

4 files changed

+143
-22
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
OrderJSON 是手动解析 json 字符串, 使得字典 key 与 json key顺序一致。
55
OrderJSON 并不是一个可以有顺序的字典。
66

7-
该项目基于[josn--swiftDo](https://github.com/swiftdo/json)基础上,修复空数组和空对象 添加扩展方法 `any()`而来,具体原理参考源项目
7+
**背景**
8+
在使用系统自带的 json 字符串转字典方法`JSONSerialization`后,发现字典是无序的。但是有时候我又想知道 key 的顺序。
9+
所以在 [json -- swiftDo](https://github.com/swiftdo/json)基础上,使得我们可以获取 json 的 key 顺序。
10+
811
参考文章
912
- [Swift 码了个 JSON 解析器(一)](https://oldbird.run/swift/fp/t3-json1.html)
1013
- [Swift 码了个 JSON 解析器(二)](https://oldbird.run/swift/fp/t3-json2.html)
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,11 @@ public struct JsonParser {
256256
private var tokenizer: JsonTokenizer
257257

258258
private init(text: String) {
259-
tokenizer = JsonTokenizer(string: text)
259+
var availableText = text
260+
if text.contains("\n") {
261+
availableText = text.replacingOccurrences(of: "\n", with: "") //去除\n [不严谨]
262+
}
263+
tokenizer = JsonTokenizer(string: availableText)
260264
}
261265

262266
static func parse(text: String) throws -> OrderJSON? {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// OrderJSON+Subkeys.swift
3+
//
4+
//
5+
// Created by Sven on 2021/4/28.
6+
//
7+
8+
import Foundation
9+
10+
/// 解析出普通 json 的有序子键
11+
public extension OrderJSON {
12+
13+
/// 返回json中当前 key 的子键数组(具有顺序)
14+
/// - Parameter keyPath: 路径数组,如`data.info` -> ["data","info"], 最顶层为空数组
15+
/// - Returns: 子健数组
16+
func subKeysFor(keyPath: [String]) -> [String] {
17+
let tempPath = keyPath.filter{ $0 != ""}
18+
guard case .object(_) = self else {
19+
return []
20+
}
21+
guard !tempPath.isEmpty else {
22+
return searchKeysIn(self)
23+
}
24+
let paths = tempPath
25+
return keysFor(paths: paths, in: self)
26+
}
27+
28+
private func keysFor(paths: [String], in json: OrderJSON) -> [String] {
29+
guard !paths.isEmpty else {
30+
print("异常错误,请检查")
31+
return [] // 应该永远不会发生
32+
}
33+
let currentKey = paths.first!
34+
let nextPaths = Array(paths.dropFirst())
35+
guard let currentOrderJSON = searchJSONFor(key: currentKey, in: json) else {
36+
print("找不到key=\(currentKey)的 OrderJSON 对象")
37+
return []
38+
}
39+
if nextPaths.isEmpty {
40+
// 结束
41+
return searchKeysIn(currentOrderJSON)
42+
} else {
43+
return keysFor(paths: nextPaths, in: currentOrderJSON)
44+
}
45+
}
46+
47+
private func searchJSONFor(key: String, in json: OrderJSON) -> OrderJSON? {
48+
guard case .object(let objc) = json else {
49+
print("\(key)不是JSON.object类型")
50+
return nil
51+
}
52+
guard let keyObject = orderJSONFor(key: key, in: objc) else {
53+
print("\(key)下不存在该 OrderJSON 对象")
54+
return nil
55+
}
56+
return keyObject
57+
}
58+
private func searchKeysIn(_ json: OrderJSON) -> [String] {
59+
if case .object(let objc) = json {
60+
// json 对象
61+
return searchKeysInObject(objc)
62+
}
63+
if case .array(let objc) = json, let first = objc.first {
64+
// 数组
65+
if case .object(let items) = first {
66+
return searchKeysInObject(items)
67+
}
68+
}
69+
return []
70+
}
71+
72+
private func searchKeysInObject(_ keyObjects:[[String: Any]]) -> [String] {
73+
var keys: [String] = []
74+
keyObjects.forEach { (value) in
75+
keys.append(value.keys.first!)
76+
}
77+
return keys
78+
}
79+
80+
private func orderJSONFor(key: String, in arr:[[String: OrderJSON]]) -> OrderJSON? {
81+
return arr.first(where: {$0.first!.key == key })?.first?.value
82+
}
83+
}

Tests/OrderJSONTests/OrderJSONTests.swift

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,32 +64,63 @@ final class OrderJSONTests: XCTestCase {
6464
let jsonObj = try? JsonParser.parse(text: json)
6565
XCTAssertNotNil(jsonObj)
6666
}
67+
68+
/// 包含转义字符
69+
//func testContactEscapeJson() {
70+
// let escapeJson = """
71+
// """
72+
// let unicodeJson = """
73+
// {"unicode":"\u4e2d\u6587"}
74+
// """
75+
// let jsonObj = try? JsonParser.parse(text: unicodeJson)
76+
// XCTAssertNotNil(jsonObj)
77+
//}
6778
// MARK: - 解析转化为 字典
68-
let yapiJSON =
79+
80+
private func transformOrderJSON() -> OrderJSON? {
81+
let json =
82+
"""
83+
{"stringValue":"字符串","intValue":20,"doubleValue":12.8,"boolValue":false,"objectValue":{"objectKey1":"value1","objectKey2":2,"objectKey3":{"key":"value"}},"arrayValue1":[1,2,3],"arrayValue2":[{"name":"小明","age":18}],"emptyArray":[],"emptyObject":{},"nullValue":null}
6984
"""
70-
{"type":"object","title":"empty object","properties":{"data":{"type":"object","properties":{"stringValue":{"type":"string","description":"字符串类型","mock":{"mock":"字符串"}},"integerValue":{"type":"integer","description":"整型数据类型","mock":{"mock":"20"}},"numberValue":{"type":"number","description":"浮点数据类型","mock":{"mock":"15.5"}},"booleanValue":{"type":"boolean","description":"布尔类型","mock":{"mock":"true"}},"arrayValue":{"type":"array","items":{"type":"string","mock":{"mock":"数组字符串"}},"description":"数组类型"}},"required":["stringValue","integerValue","numberValue","booleanValue","arrayValue"]},"code":{"type":"string","mock":{"mock":"200"}}},"required":["data","code"]}
71-
"""
72-
private func transformYapiData() -> OrderJSON? {
73-
try? JsonParser.parse(text: yapiJSON)
85+
return try? JsonParser.parse(text: json)
7486
}
7587
func testTransformYapiDataToJSON() {
76-
let jsonObj = transformYapiData()
88+
let jsonObj = transformOrderJSON()
7789
XCTAssertNotNil(jsonObj)
7890

7991
}
8092

81-
// func testJSONAnyVale() {
82-
// guard let jsonObj = transformYapiData() else {
83-
// return
84-
// }
85-
// let anyValue = jsonObj.any()
86-
// guard let dic = anyValue as? [String: Any] else {
87-
// XCTAssertTrue(true, "通过YApij son字符串创建的JSON对象, 扩展 any() 方法返回值转字必须成功")
88-
// return
89-
// }
90-
//
91-
// XCTAssertTrue(dic["type"] as! String == "object")
92-
// XCTAssertTrue(dic["required"] as! [String] == ["data","code"])
93-
// }
94-
93+
private func transformStringToArray(_ string: String) -> [String] {
94+
if string.isEmpty {
95+
return []
96+
}
97+
return string.components(separatedBy: ".")
98+
}
99+
100+
/// 测试某路径下的 key 顺序
101+
func testJSONKeys() {
102+
guard let jsonObject = transformOrderJSON() else {
103+
XCTAssertFalse(false)
104+
return
105+
}
106+
let objectValuePath = "objectValue"
107+
let rootPath = ""
108+
let rootKeys = jsonObject.subKeysFor(keyPath: transformStringToArray(rootPath))
109+
XCTAssertTrue(rootKeys == ["stringValue","intValue","doubleValue","boolValue","objectValue","arrayValue1","arrayValue2","emptyArray","emptyObject","nullValue",])
110+
let dataKeysWhenRight = ["objectKey1", "objectKey2","objectKey3"]
111+
let dataKeys = jsonObject.subKeysFor(keyPath: transformStringToArray(objectValuePath))
112+
XCTAssertTrue(dataKeys == dataKeysWhenRight)
113+
114+
let stringValuePath = "stringValue"
115+
let stringValueKeys = jsonObject.subKeysFor(keyPath: transformStringToArray(stringValuePath))
116+
XCTAssertTrue(stringValueKeys.isEmpty)
117+
118+
let objectArrayValuePath = "arrayValue2"
119+
let objectValuePathKeys = jsonObject.subKeysFor(keyPath: transformStringToArray(objectArrayValuePath))
120+
XCTAssertTrue(objectValuePathKeys == ["name","age"])
121+
122+
let multiPath = "objectValue.objectKey3"
123+
let multiPathPathKeys = jsonObject.subKeysFor(keyPath: transformStringToArray(multiPath))
124+
XCTAssertTrue(multiPathPathKeys == ["key"])
125+
}
95126
}

0 commit comments

Comments
 (0)