Skip to content

Commit d1d1538

Browse files
committed
SR-4842: Allow JSONSerialization to serialize optionals without requiring NSNull()
1 parent 79322e9 commit d1d1538

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

Foundation/NSJSONSerialization.swift

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ open class JSONSerialization : NSObject {
5656
*/
5757
open class func isValidJSONObject(_ obj: Any) -> Bool {
5858
// TODO: - revisit this once bridging story gets fully figured out
59-
func isValidJSONObjectInternal(_ obj: Any) -> Bool {
59+
func isValidJSONObjectInternal(_ obj: Any?) -> Bool {
60+
// Emulate the SE-0140 behavior bridging behavior for nils
61+
guard let obj = obj else {
62+
return true
63+
}
64+
6065
// object is Swift.String, NSNull, Int, Bool, or UInt
6166
if obj is String || obj is NSNull || obj is Int || obj is Bool || obj is UInt {
6267
return true
@@ -72,7 +77,7 @@ open class JSONSerialization : NSObject {
7277
}
7378

7479
// object is Swift.Array
75-
if let array = obj as? [Any] {
80+
if let array = obj as? [Any?] {
7681
for element in array {
7782
guard isValidJSONObjectInternal(element) else {
7883
return false
@@ -82,7 +87,7 @@ open class JSONSerialization : NSObject {
8287
}
8388

8489
// object is Swift.Dictionary
85-
if let dictionary = obj as? [String: Any] {
90+
if let dictionary = obj as? [String: Any?] {
8691
for (_, value) in dictionary {
8792
guard isValidJSONObjectInternal(value) else {
8893
return false
@@ -103,7 +108,7 @@ open class JSONSerialization : NSObject {
103108
}
104109

105110
// top level object must be an Swift.Array or Swift.Dictionary
106-
guard obj is [Any] || obj is [String: Any] else {
111+
guard obj is [Any?] || obj is [String: Any?] else {
107112
return false
108113
}
109114

@@ -308,8 +313,12 @@ private struct JSONWriter {
308313
self.writer = writer
309314
}
310315

311-
mutating func serializeJSON(_ obj: Any) throws {
316+
mutating func serializeJSON(_ obj: Any?) throws {
312317

318+
guard let obj = obj else {
319+
try serializeNull()
320+
return
321+
}
313322
// For better performance, the most expensive conditions to evaluate should be last.
314323
switch (obj) {
315324
case let str as String:
@@ -320,12 +329,12 @@ private struct JSONWriter {
320329
try serializeInt(value: num)
321330
case let num as UInt:
322331
try serializeUInt(value: num)
323-
case let array as Array<Any>:
332+
case let array as Array<Any?>:
324333
try serializeArray(array)
325-
case let dict as Dictionary<AnyHashable, Any>:
334+
case let dict as Dictionary<AnyHashable, Any?>:
326335
try serializeDictionary(dict)
327-
case let null as NSNull:
328-
try serializeNull(null)
336+
case is NSNull:
337+
try serializeNull()
329338
case _ where _SwiftValue.store(obj) is NSNumber:
330339
try serializeNumber(_SwiftValue.store(obj) as! NSNumber)
331340
default:
@@ -471,7 +480,7 @@ private struct JSONWriter {
471480
}
472481
}
473482

474-
mutating func serializeArray(_ array: [Any]) throws {
483+
mutating func serializeArray(_ array: [Any?]) throws {
475484
writer("[")
476485
if pretty {
477486
writer("\n")
@@ -497,7 +506,7 @@ private struct JSONWriter {
497506
writer("]")
498507
}
499508

500-
mutating func serializeDictionary(_ dict: Dictionary<AnyHashable, Any>) throws {
509+
mutating func serializeDictionary(_ dict: Dictionary<AnyHashable, Any?>) throws {
501510
writer("{")
502511
if pretty {
503512
writer("\n")
@@ -506,7 +515,7 @@ private struct JSONWriter {
506515

507516
var first = true
508517

509-
func serializeDictionaryElement(key: AnyHashable, value: Any) throws {
518+
func serializeDictionaryElement(key: AnyHashable, value: Any?) throws {
510519
if first {
511520
first = false
512521
} else if pretty {
@@ -553,7 +562,7 @@ private struct JSONWriter {
553562
writer("}")
554563
}
555564

556-
func serializeNull(_ null: NSNull) throws {
565+
func serializeNull() throws {
557566
writer("null")
558567
}
559568

TestFoundation/TestNSJSONSerialization.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,11 +872,16 @@ extension TestNSJSONSerialization {
872872
"0",
873873
NSNumber(value: Int(0))
874874
)
875-
)
875+
),
876876
]
877877
for testCase in trueJSON {
878878
XCTAssertTrue(JSONSerialization.isValidJSONObject(testCase))
879879
}
880+
881+
// [Any?.none]
882+
let optionalAny: Any? = nil
883+
let anyArray: [Any] = [optionalAny as Any]
884+
XCTAssertTrue(JSONSerialization.isValidJSONObject(anyArray))
880885
}
881886

882887
func test_isValidJSONObjectFalse() {
@@ -1069,6 +1074,24 @@ extension TestNSJSONSerialization {
10691074

10701075
let dict2 = [["a":NSNull()], ["b":NSNull()], ["c":NSNull()]]
10711076
XCTAssertEqual(try trySerialize(dict2), "[{\"a\":null},{\"b\":null},{\"c\":null}]")
1077+
1078+
let arr3 = [nil] as [Any?]
1079+
XCTAssertEqual(try trySerialize(arr3), "[null]")
1080+
1081+
let dict3 = ["a":nil] as [String: Any?]
1082+
XCTAssertEqual(try trySerialize(dict3), "{\"a\":null}")
1083+
1084+
let arr4 = [nil, nil, nil] as [Any?]
1085+
XCTAssertEqual(try trySerialize(arr4), "[null,null,null]")
1086+
1087+
let dict4 = [["a": nil] as [String: Any?], ["b": nil] as [String: Any?], ["c": nil] as [String: Any?]]
1088+
XCTAssertEqual(try trySerialize(dict4), "[{\"a\":null},{\"b\":null},{\"c\":null}]")
1089+
1090+
let arr5 = [Optional<Any>.none]
1091+
XCTAssertEqual(try trySerialize(arr5), "[null]")
1092+
1093+
let arr6: Array<Optional<Any>> = [Bool?.none, String?.none, Int?.none, [Any?]?.none]
1094+
XCTAssertEqual(try trySerialize(arr6), "[null,null,null,null]")
10721095
}
10731096

10741097
func test_serialize_complexObject() {
@@ -1117,6 +1140,9 @@ extension TestNSJSONSerialization {
11171140

11181141
dict = ["a":["b":["c":["d":1]]]]
11191142
XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":{\"d\":1}}}}")
1143+
1144+
dict = ["a":["b":["c":[1, Optional<Any>.none]]]]
1145+
XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":[1,null]}}}")
11201146
}
11211147

11221148
func test_serialize_number() {

0 commit comments

Comments
 (0)