@@ -92,6 +92,17 @@ class TestJSONEncoder : TestJSONEncoderSuper {
92
92
_testRoundTrip ( of: employee)
93
93
}
94
94
95
+ func testEncodingTopLevelNullableType( ) {
96
+ // EnhancedBool is a type which encodes either as a Bool or as nil.
97
+ _testEncodeFailure ( of: EnhancedBool . true)
98
+ _testEncodeFailure ( of: EnhancedBool . false)
99
+ _testEncodeFailure ( of: EnhancedBool . fileNotFound)
100
+
101
+ _testRoundTrip ( of: TopLevelWrapper ( EnhancedBool . true) , expectedJSON: " { \" value \" :true} " . data ( using: . utf8) !)
102
+ _testRoundTrip ( of: TopLevelWrapper ( EnhancedBool . false) , expectedJSON: " { \" value \" :false} " . data ( using: . utf8) !)
103
+ _testRoundTrip ( of: TopLevelWrapper ( EnhancedBool . fileNotFound) , expectedJSON: " { \" value \" :null} " . data ( using: . utf8) !)
104
+ }
105
+
95
106
// MARK: - Output Formatting Tests
96
107
func testEncodingOutputFormattingDefault( ) {
97
108
let expectedJSON = " { \" name \" : \" Johnny Appleseed \" , \" email \" : \" [email protected] \" } " . data ( using
: . utf8
) !
@@ -650,61 +661,30 @@ fileprivate struct Company : Codable, Equatable {
650
661
}
651
662
}
652
663
653
- // MARK: - Helper Types
654
-
655
- /// Wraps a type T so that it can be encoded at the top level of a payload.
656
- fileprivate struct TopLevelWrapper < T> : Codable , Equatable where T : Codable , T : Equatable {
657
- let value : T
658
-
659
- init ( _ value: T ) {
660
- self . value = value
661
- }
662
-
663
- static func == ( _ lhs: TopLevelWrapper < T > , _ rhs: TopLevelWrapper < T > ) -> Bool {
664
- return lhs. value == rhs. value
665
- }
666
- }
667
-
668
- fileprivate struct FloatNaNPlaceholder : Codable , Equatable {
669
- init ( ) { }
670
-
671
- func encode( to encoder: Encoder ) throws {
672
- var container = encoder. singleValueContainer ( )
673
- try container. encode ( Float . nan)
674
- }
664
+ /// An enum type which decodes from Bool?.
665
+ fileprivate enum EnhancedBool : Codable {
666
+ case `true`
667
+ case `false`
668
+ case fileNotFound
675
669
676
670
init ( from decoder: Decoder ) throws {
677
671
let container = try decoder. singleValueContainer ( )
678
- let float = try container. decode ( Float . self)
679
- if !float. isNaN {
680
- throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath: decoder. codingPath, debugDescription: " Couldn't decode NaN. " ) )
672
+ if container. decodeNil ( ) {
673
+ self = . fileNotFound
674
+ } else {
675
+ let value = try container. decode ( Bool . self)
676
+ self = value ? . true : . false
681
677
}
682
678
}
683
679
684
- static func == ( _ lhs: FloatNaNPlaceholder , _ rhs: FloatNaNPlaceholder ) -> Bool {
685
- return true
686
- }
687
- }
688
-
689
- fileprivate struct DoubleNaNPlaceholder : Codable , Equatable {
690
- init ( ) { }
691
-
692
680
func encode( to encoder: Encoder ) throws {
693
681
var container = encoder. singleValueContainer ( )
694
- try container. encode ( Double . nan)
695
- }
696
-
697
- init ( from decoder: Decoder ) throws {
698
- let container = try decoder. singleValueContainer ( )
699
- let double = try container. decode ( Double . self)
700
- if !double. isNaN {
701
- throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath: decoder. codingPath, debugDescription: " Couldn't decode NaN. " ) )
682
+ switch self {
683
+ case . true : try container. encode ( true )
684
+ case . false : try container. encode ( false )
685
+ case . fileNotFound: try container. encodeNil ( )
702
686
}
703
687
}
704
-
705
- static func == ( _ lhs: DoubleNaNPlaceholder , _ rhs: DoubleNaNPlaceholder ) -> Bool {
706
- return true
707
- }
708
688
}
709
689
710
690
/// A type which encodes as an array directly through a single value container.
@@ -853,6 +833,63 @@ struct NestedContainersTestType : Encodable {
853
833
}
854
834
}
855
835
836
+ // MARK: - Helper Types
837
+
838
+ /// Wraps a type T so that it can be encoded at the top level of a payload.
839
+ fileprivate struct TopLevelWrapper < T> : Codable , Equatable where T : Codable , T : Equatable {
840
+ let value : T
841
+
842
+ init ( _ value: T ) {
843
+ self . value = value
844
+ }
845
+
846
+ static func == ( _ lhs: TopLevelWrapper < T > , _ rhs: TopLevelWrapper < T > ) -> Bool {
847
+ return lhs. value == rhs. value
848
+ }
849
+ }
850
+
851
+ fileprivate struct FloatNaNPlaceholder : Codable , Equatable {
852
+ init ( ) { }
853
+
854
+ func encode( to encoder: Encoder ) throws {
855
+ var container = encoder. singleValueContainer ( )
856
+ try container. encode ( Float . nan)
857
+ }
858
+
859
+ init ( from decoder: Decoder ) throws {
860
+ let container = try decoder. singleValueContainer ( )
861
+ let float = try container. decode ( Float . self)
862
+ if !float. isNaN {
863
+ throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath: decoder. codingPath, debugDescription: " Couldn't decode NaN. " ) )
864
+ }
865
+ }
866
+
867
+ static func == ( _ lhs: FloatNaNPlaceholder , _ rhs: FloatNaNPlaceholder ) -> Bool {
868
+ return true
869
+ }
870
+ }
871
+
872
+ fileprivate struct DoubleNaNPlaceholder : Codable , Equatable {
873
+ init ( ) { }
874
+
875
+ func encode( to encoder: Encoder ) throws {
876
+ var container = encoder. singleValueContainer ( )
877
+ try container. encode ( Double . nan)
878
+ }
879
+
880
+ init ( from decoder: Decoder ) throws {
881
+ let container = try decoder. singleValueContainer ( )
882
+ let double = try container. decode ( Double . self)
883
+ if !double. isNaN {
884
+ throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath: decoder. codingPath, debugDescription: " Couldn't decode NaN. " ) )
885
+ }
886
+ }
887
+
888
+ static func == ( _ lhs: DoubleNaNPlaceholder , _ rhs: DoubleNaNPlaceholder ) -> Bool {
889
+ return true
890
+ }
891
+ }
892
+
856
893
// MARK: - Run Tests
857
894
858
895
#if !FOUNDATION_XCTEST
@@ -870,6 +907,7 @@ JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncode
870
907
JSONEncoderTests . test ( " testEncodingTopLevelStructuredClass " ) { TestJSONEncoder ( ) . testEncodingTopLevelStructuredClass ( ) }
871
908
JSONEncoderTests . test ( " testEncodingTopLevelDeepStructuredType " ) { TestJSONEncoder ( ) . testEncodingTopLevelDeepStructuredType ( ) }
872
909
JSONEncoderTests . test ( " testEncodingClassWhichSharesEncoderWithSuper " ) { TestJSONEncoder ( ) . testEncodingClassWhichSharesEncoderWithSuper ( ) }
910
+ JSONEncoderTests . test ( " testEncodingTopLevelNullableType " ) { TestJSONEncoder ( ) . testEncodingTopLevelNullableType ( ) }
873
911
JSONEncoderTests . test ( " testEncodingOutputFormattingDefault " ) { TestJSONEncoder ( ) . testEncodingOutputFormattingDefault ( ) }
874
912
JSONEncoderTests . test ( " testEncodingOutputFormattingPrettyPrinted " ) { TestJSONEncoder ( ) . testEncodingOutputFormattingPrettyPrinted ( ) }
875
913
JSONEncoderTests . test ( " testEncodingOutputFormattingSortedKeys " ) { TestJSONEncoder ( ) . testEncodingOutputFormattingSortedKeys ( ) }
0 commit comments