Skip to content

Commit 29ebf37

Browse files
authored
Add more dictionary subscripts (#2)
Add dictionary subscripts to directly access the concrete values wrapped by JSON.
1 parent 20fd696 commit 29ebf37

File tree

2 files changed

+246
-3
lines changed

2 files changed

+246
-3
lines changed

Sources/JSON/JSON.swift

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,102 @@ public enum JSON:
3232
}
3333
}
3434

35+
public subscript(key: String) -> [Any?]? {
36+
get {
37+
guard case let .dictionary(dict) = self else { return nil }
38+
return dict[key]?.arrayValue
39+
}
40+
set {
41+
guard case var .dictionary(dict) = self else { return }
42+
if let newValue {
43+
dict[key] = .array(newValue.compactMap(\.json))
44+
} else {
45+
dict.removeValue(forKey: key)
46+
}
47+
self = .dictionary(dict)
48+
}
49+
}
50+
51+
public subscript(key: String) -> Bool? {
52+
get {
53+
guard case let .dictionary(dict) = self else { return nil }
54+
return dict[key]?.boolValue
55+
}
56+
set {
57+
guard case var .dictionary(dict) = self else { return }
58+
if let newValue {
59+
dict[key] = .boolean(newValue)
60+
} else {
61+
dict.removeValue(forKey: key)
62+
}
63+
self = .dictionary(dict)
64+
}
65+
}
66+
67+
public subscript(key: String) -> [String: Any?]? {
68+
get {
69+
guard case let .dictionary(dict) = self else { return nil }
70+
return dict[key]?.dictionaryValue
71+
}
72+
set {
73+
guard case var .dictionary(dict) = self else { return }
74+
if let newValue {
75+
dict[key] = .dictionary(newValue.compactMapValues(\.json))
76+
} else {
77+
dict.removeValue(forKey: key)
78+
}
79+
self = .dictionary(dict)
80+
}
81+
}
82+
83+
public subscript(key: String) -> Double? {
84+
get {
85+
guard case let .dictionary(dict) = self else { return nil }
86+
return dict[key]?.doubleValue
87+
}
88+
set {
89+
guard case var .dictionary(dict) = self else { return }
90+
if let newValue {
91+
dict[key] = .number(newValue)
92+
} else {
93+
dict.removeValue(forKey: key)
94+
}
95+
self = .dictionary(dict)
96+
}
97+
}
98+
99+
public subscript(key: String) -> Int? {
100+
get {
101+
guard case let .dictionary(dict) = self else { return nil }
102+
return dict[key]?.integerValue
103+
}
104+
set {
105+
guard case var .dictionary(dict) = self else { return }
106+
if let newValue {
107+
dict[key] = .number(Double(newValue))
108+
} else {
109+
dict.removeValue(forKey: key)
110+
}
111+
self = .dictionary(dict)
112+
}
113+
}
114+
115+
public subscript(key: String) -> String? {
116+
get {
117+
guard case let .dictionary(dict) = self else { return nil }
118+
return dict[key]?.stringValue
119+
}
120+
set {
121+
guard case var .dictionary(dict) = self else { return }
122+
if let newValue {
123+
dict[key] = .string(newValue)
124+
} else {
125+
dict.removeValue(forKey: key)
126+
}
127+
self = .dictionary(dict)
128+
}
129+
}
130+
35131
public subscript(index: Int) -> JSON? {
36132
get {
37133
guard case let .array(arr) = self, index < arr.count
@@ -216,6 +312,102 @@ public extension JSON? {
216312
}
217313
}
218314

315+
subscript(key: String) -> [Any?]? {
316+
get {
317+
guard case let .dictionary(dict) = self else { return nil }
318+
return dict[key]?.arrayValue
319+
}
320+
set {
321+
guard case var .dictionary(dict) = self else { return }
322+
if let newValue {
323+
dict[key] = .array(newValue.compactMap(\.json))
324+
} else {
325+
dict.removeValue(forKey: key)
326+
}
327+
self = .dictionary(dict)
328+
}
329+
}
330+
331+
subscript(key: String) -> Bool? {
332+
get {
333+
guard case let .dictionary(dict) = self else { return nil }
334+
return dict[key]?.boolValue
335+
}
336+
set {
337+
guard case var .dictionary(dict) = self else { return }
338+
if let newValue {
339+
dict[key] = .boolean(newValue)
340+
} else {
341+
dict.removeValue(forKey: key)
342+
}
343+
self = .dictionary(dict)
344+
}
345+
}
346+
347+
subscript(key: String) -> [String: Any?]? {
348+
get {
349+
guard case let .dictionary(dict) = self else { return nil }
350+
return dict[key]?.dictionaryValue
351+
}
352+
set {
353+
guard case var .dictionary(dict) = self else { return }
354+
if let newValue {
355+
dict[key] = .dictionary(newValue.compactMapValues(\.json))
356+
} else {
357+
dict.removeValue(forKey: key)
358+
}
359+
self = .dictionary(dict)
360+
}
361+
}
362+
363+
subscript(key: String) -> Double? {
364+
get {
365+
guard case let .dictionary(dict) = self else { return nil }
366+
return dict[key]?.doubleValue
367+
}
368+
set {
369+
guard case var .dictionary(dict) = self else { return }
370+
if let newValue {
371+
dict[key] = .number(newValue)
372+
} else {
373+
dict.removeValue(forKey: key)
374+
}
375+
self = .dictionary(dict)
376+
}
377+
}
378+
379+
subscript(key: String) -> Int? {
380+
get {
381+
guard case let .dictionary(dict) = self else { return nil }
382+
return dict[key]?.integerValue
383+
}
384+
set {
385+
guard case var .dictionary(dict) = self else { return }
386+
if let newValue {
387+
dict[key] = .number(Double(newValue))
388+
} else {
389+
dict.removeValue(forKey: key)
390+
}
391+
self = .dictionary(dict)
392+
}
393+
}
394+
395+
subscript(key: String) -> String? {
396+
get {
397+
guard case let .dictionary(dict) = self else { return nil }
398+
return dict[key]?.stringValue
399+
}
400+
set {
401+
guard case var .dictionary(dict) = self else { return }
402+
if let newValue {
403+
dict[key] = .string(newValue)
404+
} else {
405+
dict.removeValue(forKey: key)
406+
}
407+
self = .dictionary(dict)
408+
}
409+
}
410+
219411
subscript(index: Int) -> JSON? {
220412
get {
221413
guard case let .array(arr) = self, index < arr.count

Tests/JSONTests/JSONTests.swift

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ final class JSONTests: XCTestCase {
5555
],
5656
]
5757
XCTAssertTrue(object["one"] == 1)
58+
XCTAssertEqual(1, object["one"])
5859
XCTAssertFalse(object["one"] == "one")
60+
XCTAssertNotEqual("one", object["one"])
5961
XCTAssertTrue(object["bool"] == true)
62+
XCTAssertEqual(true, object["bool"])
6063
XCTAssertFalse(object["bool"] == 2.0)
64+
XCTAssertNotEqual(2.0, object["bool"])
6165
XCTAssertTrue(JSON(["key": "value"]) == object["dict"])
6266
XCTAssertFalse(JSON.array([.string("one"), .boolean(false)]) == object["dict"])
63-
XCTAssertNil(object["doesNotExist"])
67+
XCTAssertNil(object["doesNotExist"] as JSON?)
6468
XCTAssertEqual("value", object["dict"]["key"]?.stringValue)
6569

6670
let array = JSON(["one", 2, false])
@@ -70,7 +74,51 @@ final class JSONTests: XCTestCase {
7074
XCTAssertNil(array[3])
7175

7276
let string: JSON = .string("text")
73-
XCTAssertNil(string["text"])
77+
XCTAssertNil(string["text"] as JSON?)
78+
}
79+
80+
func testGetConcreteTypeUsingSubscript() throws {
81+
let object: JSON = [
82+
"one": 1.0,
83+
"bool": true,
84+
"dict": [
85+
"key": "value",
86+
"double": 3.14,
87+
] as [String: Any]
88+
]
89+
90+
let one: Int? = object["one"]
91+
XCTAssertEqual(1, one)
92+
let notOne: String? = object["one"]
93+
XCTAssertNil(notOne)
94+
95+
let tru: Bool? = object["bool"]
96+
XCTAssertEqual(true, tru)
97+
let notTru: Double? = object["bool"]
98+
XCTAssertNil(notTru)
99+
100+
let dict: [String: Any?]? = object["dict"]
101+
XCTAssertEqual("value", dict?["key"] as? String)
102+
XCTAssertEqual(3.14, dict?["double"] as? Double)
103+
let notDict: [Any?]? = object["dict"]
104+
XCTAssertNil(notDict)
105+
106+
let val: String? = object["dict"]["key"]
107+
XCTAssertEqual("value", val)
108+
let notVal: [Any?]? = object["dict"]["key"]
109+
XCTAssertNil(notVal)
110+
111+
if let val: String = object["dict"]["key"] {
112+
XCTAssertEqual("value", val)
113+
} else {
114+
XCTFail("Should have unwrapped value")
115+
}
116+
117+
if let doub: Double = object["dict"]["double"] {
118+
XCTAssertEqual(3.14, doub)
119+
} else {
120+
XCTFail("Should have unwrapped value")
121+
}
74122
}
75123

76124
func testSetSubscript() throws {
@@ -97,7 +145,10 @@ final class JSONTests: XCTestCase {
97145
XCTAssertEqual(["one", nil, 3.14, true], array)
98146

99147
array[1] = ["bingo", nil, 3]
100-
XCTAssertEqual(["one", ["bingo", nil, 3], 3.14, true], array)
148+
XCTAssertEqual(
149+
["one", ["bingo", nil, 3] as [Any?], 3.14, true],
150+
array
151+
)
101152
}
102153

103154
func testRawValue() throws {

0 commit comments

Comments
 (0)