@@ -8,7 +8,6 @@ class MapTests: XCTestCase {
88 XCTAssertEqual ( try Map . bool ( false ) . boolValue ( ) , false )
99 XCTAssertEqual ( try Map . bool ( true ) . boolValue ( ) , true )
1010 XCTAssertEqual ( try Map . string ( " Hello world " ) . stringValue ( ) , " Hello world " )
11-
1211 }
1312
1413 func testOptionalConversion( ) {
@@ -36,16 +35,117 @@ class MapTests: XCTestCase {
3635 [
3736 " first " : . number( 1 ) ,
3837 " second " : . number( 4 ) ,
39- " third " : . number( 9 )
38+ " third " : . number( 9 ) ,
39+ " fourth " : . null,
40+ " fifth " : . undefined
4041 ]
4142 )
42- XCTAssertEqual ( map. dictionary? . count, 3 )
43+ XCTAssertEqual ( map. dictionary? . count, 5 )
4344
4445 let dictionary = try map. dictionaryValue ( )
4546
46- XCTAssertEqual ( dictionary. count, 3 )
47+ XCTAssertEqual ( dictionary. count, 5 )
4748 XCTAssertEqual ( try dictionary [ " first " ] ? . intValue ( ) , 1 )
4849 XCTAssertEqual ( try dictionary [ " second " ] ? . intValue ( ) , 4 )
4950 XCTAssertEqual ( try dictionary [ " third " ] ? . intValue ( ) , 9 )
51+ XCTAssertEqual ( dictionary [ " fourth " ] ? . isNull, true )
52+ XCTAssertEqual ( dictionary [ " fifth " ] ? . isUndefined, true )
53+ }
54+
55+ // Ensure that default decoding preserves undefined becoming nil
56+ func testNilAndUndefinedDecodeToNilByDefault( ) throws {
57+ struct DecodableTest : Codable {
58+ let first : Int ?
59+ let second : Int ?
60+ let third : Int ?
61+ let fourth : Int ?
62+ }
63+
64+ let map = Map . dictionary (
65+ [
66+ " first " : . number( 1 ) ,
67+ " second " : . null,
68+ " third " : . undefined
69+ // fourth not included
70+ ]
71+ )
72+
73+ let decodable = try MapDecoder ( ) . decode ( DecodableTest . self, from: map)
74+ XCTAssertEqual ( decodable. first, 1 )
75+ XCTAssertEqual ( decodable. second, nil )
76+ XCTAssertEqual ( decodable. third, nil )
77+ XCTAssertEqual ( decodable. fourth, nil )
78+ }
79+
80+ // Ensure that, if custom decoding is defined, provided nulls and unset values can be differentiated.
81+ // This should match JSON in that values set to `null` should be 'contained' by the container, but
82+ // values expected by the result that are undefined or not present should not be.
83+ func testNilAndUndefinedDecoding( ) throws {
84+ struct DecodableTest : Codable {
85+ let first : Int ?
86+ let second : Int ?
87+ let third : Int ?
88+ let fourth : Int ?
89+
90+ init (
91+ first: Int ? ,
92+ second: Int ? ,
93+ third: Int ? ,
94+ fourth: Int ?
95+ ) {
96+ self . first = first
97+ self . second = second
98+ self . third = third
99+ self . fourth = fourth
100+ }
101+
102+ init ( from decoder: Decoder ) throws {
103+ let container = try decoder. container ( keyedBy: CodingKeys . self)
104+
105+ XCTAssertTrue ( container. contains ( . first) )
106+ // Null value should be contained, but decode to nil
107+ XCTAssertTrue ( container. contains ( . second) )
108+ // Undefined value should not be contained
109+ XCTAssertFalse ( container. contains ( . third) )
110+ // Missing value should operate the same as undefined
111+ XCTAssertFalse ( container. contains ( . fourth) )
112+
113+ first = try container. decodeIfPresent ( Int . self, forKey: . first)
114+ second = try container. decodeIfPresent ( Int . self, forKey: . second)
115+ third = try container. decodeIfPresent ( Int . self, forKey: . third)
116+ fourth = try container. decodeIfPresent ( Int . self, forKey: . fourth)
117+ }
118+ }
119+
120+ let map = Map . dictionary (
121+ [
122+ " first " : . number( 1 ) ,
123+ " second " : . null,
124+ " third " : . undefined
125+ // fourth not included
126+ ]
127+ )
128+
129+ _ = try MapDecoder ( ) . decode ( DecodableTest . self, from: map)
130+ }
131+
132+ // Ensure that map encoding includes defined nulls, but skips undefined values
133+ func testMapEncoding( ) throws {
134+ let map = Map . dictionary (
135+ [
136+ " first " : . number( 1 ) ,
137+ " second " : . null,
138+ " third " : . undefined
139+ ]
140+ )
141+
142+ let data = try JSONEncoder ( ) . encode ( map)
143+ let json = String ( data: data, encoding: . utf8)
144+ XCTAssertEqual (
145+ json,
146+ """
147+ { " first " :1, " second " :null}
148+ """
149+ )
50150 }
51151}
0 commit comments