Skip to content

Commit 5daa71c

Browse files
authored
Merge pull request #10249 from itaiferber/master
Allow SingleValueContainers to decode collections
2 parents cdab4de + 48d183e commit 5daa71c

File tree

4 files changed

+235
-64
lines changed

4 files changed

+235
-64
lines changed

stdlib/public/SDK/Foundation/JSONEncoder.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -952,18 +952,6 @@ fileprivate class _JSONDecoder : Decoder {
952952
}
953953

954954
func singleValueContainer() throws -> SingleValueDecodingContainer {
955-
guard !(self.storage.topContainer is [String : Any]) else {
956-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
957-
DecodingError.Context(codingPath: self.codingPath,
958-
debugDescription: "Cannot get single value decoding container -- found keyed container instead."))
959-
}
960-
961-
guard !(self.storage.topContainer is [Any]) else {
962-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
963-
DecodingError.Context(codingPath: self.codingPath,
964-
debugDescription: "Cannot get single value decoding container -- found unkeyed container instead."))
965-
}
966-
967955
return self
968956
}
969957
}

stdlib/public/SDK/Foundation/PlistEncoder.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -733,18 +733,6 @@ fileprivate class _PlistDecoder : Decoder {
733733
}
734734

735735
func singleValueContainer() throws -> SingleValueDecodingContainer {
736-
guard !(self.storage.topContainer is [String : Any]) else {
737-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
738-
DecodingError.Context(codingPath: self.codingPath,
739-
debugDescription: "Cannot get single value decoding container -- found keyed container instead."))
740-
}
741-
742-
guard !(self.storage.topContainer is [Any]) else {
743-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
744-
DecodingError.Context(codingPath: self.codingPath,
745-
debugDescription: "Cannot get single value decoding container -- found unkeyed container instead."))
746-
}
747-
748736
return self
749737
}
750738
}

test/stdlib/TestJSONEncoder.swift

Lines changed: 108 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,19 @@ class TestJSONEncoder : TestJSONEncoderSuper {
3939
func testEncodingTopLevelSingleValueEnum() {
4040
_testEncodeFailure(of: Switch.off)
4141
_testEncodeFailure(of: Switch.on)
42+
43+
_testRoundTrip(of: TopLevelWrapper(Switch.off))
44+
_testRoundTrip(of: TopLevelWrapper(Switch.on))
4245
}
4346

4447
func testEncodingTopLevelSingleValueStruct() {
4548
_testEncodeFailure(of: Timestamp(3141592653))
49+
_testRoundTrip(of: TopLevelWrapper(Timestamp(3141592653)))
4650
}
4751

4852
func testEncodingTopLevelSingleValueClass() {
4953
_testEncodeFailure(of: Counter())
54+
_testRoundTrip(of: TopLevelWrapper(Counter()))
5055
}
5156

5257
// MARK: - Encoding Top-Level Structured Types
@@ -63,6 +68,18 @@ class TestJSONEncoder : TestJSONEncoderSuper {
6368
_testRoundTrip(of: person, expectedJSON: expectedJSON)
6469
}
6570

71+
func testEncodingTopLevelStructuredSingleStruct() {
72+
// Numbers is a struct which encodes as an array through a single value container.
73+
let numbers = Numbers.testValue
74+
_testRoundTrip(of: numbers)
75+
}
76+
77+
func testEncodingTopLevelStructuredSingleClass() {
78+
// Mapping is a class which encodes as a dictionary through a single value container.
79+
let mapping = Mapping.testValue
80+
_testRoundTrip(of: mapping)
81+
}
82+
6683
func testEncodingTopLevelDeepStructuredType() {
6784
// Company is a type with fields which are Codable themselves.
6885
let company = Company.testValue
@@ -295,7 +312,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
295312
encoder.nonConformingFloatEncodingStrategy = nonConformingFloatEncodingStrategy
296313
payload = try encoder.encode(value)
297314
} catch {
298-
expectUnreachable("Failed to encode \(T.self) to JSON.")
315+
expectUnreachable("Failed to encode \(T.self) to JSON: \(error)")
299316
}
300317

301318
if let expectedJSON = json {
@@ -310,7 +327,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
310327
let decoded = try decoder.decode(T.self, from: payload)
311328
expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.")
312329
} catch {
313-
expectUnreachable("Failed to decode \(T.self) from JSON.")
330+
expectUnreachable("Failed to decode \(T.self) from JSON: \(error)")
314331
}
315332
}
316333
}
@@ -398,7 +415,7 @@ fileprivate enum Switch : Codable {
398415
}
399416

400417
/// A simple timestamp type that encodes as a single Double value.
401-
fileprivate struct Timestamp : Codable {
418+
fileprivate struct Timestamp : Codable, Equatable {
402419
let value: Double
403420

404421
init(_ value: Double) {
@@ -414,10 +431,14 @@ fileprivate struct Timestamp : Codable {
414431
var container = encoder.singleValueContainer()
415432
try container.encode(self.value)
416433
}
434+
435+
static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool {
436+
return lhs.value == rhs.value
437+
}
417438
}
418439

419440
/// A simple referential counter type that encodes as a single Int value.
420-
fileprivate final class Counter : Codable {
441+
fileprivate final class Counter : Codable, Equatable {
421442
var count: Int = 0
422443

423444
init() {}
@@ -431,6 +452,10 @@ fileprivate final class Counter : Codable {
431452
var container = encoder.singleValueContainer()
432453
try container.encode(self.count)
433454
}
455+
456+
static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool {
457+
return lhs === rhs || lhs.count == rhs.count
458+
}
434459
}
435460

436461
// MARK: - Structured Types
@@ -581,6 +606,62 @@ fileprivate struct DoubleNaNPlaceholder : Codable, Equatable {
581606
}
582607
}
583608

609+
/// A type which encodes as an array directly through a single value container.
610+
struct Numbers : Codable, Equatable {
611+
let values = [4, 8, 15, 16, 23, 42]
612+
613+
init() {}
614+
615+
init(from decoder: Decoder) throws {
616+
let container = try decoder.singleValueContainer()
617+
let decodedValues = try container.decode([Int].self)
618+
guard decodedValues == values else {
619+
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!"))
620+
}
621+
}
622+
623+
func encode(to encoder: Encoder) throws {
624+
var container = encoder.singleValueContainer()
625+
try container.encode(values)
626+
}
627+
628+
static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool {
629+
return lhs.values == rhs.values
630+
}
631+
632+
static var testValue: Numbers {
633+
return Numbers()
634+
}
635+
}
636+
637+
/// A type which encodes as a dictionary directly through a single value container.
638+
fileprivate final class Mapping : Codable, Equatable {
639+
let values: [String : URL]
640+
641+
init(values: [String : URL]) {
642+
self.values = values
643+
}
644+
645+
init(from decoder: Decoder) throws {
646+
let container = try decoder.singleValueContainer()
647+
values = try container.decode([String : URL].self)
648+
}
649+
650+
func encode(to encoder: Encoder) throws {
651+
var container = encoder.singleValueContainer()
652+
try container.encode(values)
653+
}
654+
655+
static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool {
656+
return lhs === rhs || lhs.values == rhs.values
657+
}
658+
659+
static var testValue: Mapping {
660+
return Mapping(values: ["Apple": URL(string: "http://apple.com")!,
661+
"localhost": URL(string: "http://127.0.0.1")!])
662+
}
663+
}
664+
584665
struct NestedContainersTestType : Encodable {
585666
let testSuperEncoder: Bool
586667

@@ -675,28 +756,29 @@ struct NestedContainersTestType : Encodable {
675756

676757
#if !FOUNDATION_XCTEST
677758
var JSONEncoderTests = TestSuite("TestJSONEncoder")
678-
JSONEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestJSONEncoder().testEncodingTopLevelEmptyStruct() }
679-
JSONEncoderTests.test("testEncodingTopLevelEmptyClass") { TestJSONEncoder().testEncodingTopLevelEmptyClass() }
680-
JSONEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestJSONEncoder().testEncodingTopLevelSingleValueEnum() }
681-
JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder().testEncodingTopLevelSingleValueStruct() }
682-
JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() }
683-
JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() }
684-
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
685-
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
759+
JSONEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestJSONEncoder().testEncodingTopLevelEmptyStruct() }
760+
JSONEncoderTests.test("testEncodingTopLevelEmptyClass") { TestJSONEncoder().testEncodingTopLevelEmptyClass() }
761+
JSONEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestJSONEncoder().testEncodingTopLevelSingleValueEnum() }
762+
JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder().testEncodingTopLevelSingleValueStruct() }
763+
JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() }
764+
JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() }
765+
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
766+
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleStruct") { TestJSONEncoder().testEncodingTopLevelStructuredSingleStruct() }
767+
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleClass") { TestJSONEncoder().testEncodingTopLevelStructuredSingleClass() }
686768
JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncoder().testEncodingTopLevelDeepStructuredType()}
687-
JSONEncoderTests.test("testEncodingDate") { TestJSONEncoder().testEncodingDate() }
688-
JSONEncoderTests.test("testEncodingDateSecondsSince1970") { TestJSONEncoder().testEncodingDateSecondsSince1970() }
689-
JSONEncoderTests.test("testEncodingDateMillisecondsSince1970") { TestJSONEncoder().testEncodingDateMillisecondsSince1970() }
690-
JSONEncoderTests.test("testEncodingDateISO8601") { TestJSONEncoder().testEncodingDateISO8601() }
691-
JSONEncoderTests.test("testEncodingDateFormatted") { TestJSONEncoder().testEncodingDateFormatted() }
692-
JSONEncoderTests.test("testEncodingDateCustom") { TestJSONEncoder().testEncodingDateCustom() }
693-
JSONEncoderTests.test("testEncodingDateCustomEmpty") { TestJSONEncoder().testEncodingDateCustomEmpty() }
694-
JSONEncoderTests.test("testEncodingBase64Data") { TestJSONEncoder().testEncodingBase64Data() }
695-
JSONEncoderTests.test("testEncodingCustomData") { TestJSONEncoder().testEncodingCustomData() }
696-
JSONEncoderTests.test("testEncodingCustomDataEmpty") { TestJSONEncoder().testEncodingCustomDataEmpty() }
697-
JSONEncoderTests.test("testEncodingNonConformingFloats") { TestJSONEncoder().testEncodingNonConformingFloats() }
698-
JSONEncoderTests.test("testEncodingNonConformingFloatStrings") { TestJSONEncoder().testEncodingNonConformingFloatStrings() }
699-
JSONEncoderTests.test("testNestedContainerCodingPaths") { TestJSONEncoder().testNestedContainerCodingPaths() }
700-
JSONEncoderTests.test("testSuperEncoderCodingPaths") { TestJSONEncoder().testSuperEncoderCodingPaths() }
769+
JSONEncoderTests.test("testEncodingDate") { TestJSONEncoder().testEncodingDate() }
770+
JSONEncoderTests.test("testEncodingDateSecondsSince1970") { TestJSONEncoder().testEncodingDateSecondsSince1970() }
771+
JSONEncoderTests.test("testEncodingDateMillisecondsSince1970") { TestJSONEncoder().testEncodingDateMillisecondsSince1970() }
772+
JSONEncoderTests.test("testEncodingDateISO8601") { TestJSONEncoder().testEncodingDateISO8601() }
773+
JSONEncoderTests.test("testEncodingDateFormatted") { TestJSONEncoder().testEncodingDateFormatted() }
774+
JSONEncoderTests.test("testEncodingDateCustom") { TestJSONEncoder().testEncodingDateCustom() }
775+
JSONEncoderTests.test("testEncodingDateCustomEmpty") { TestJSONEncoder().testEncodingDateCustomEmpty() }
776+
JSONEncoderTests.test("testEncodingBase64Data") { TestJSONEncoder().testEncodingBase64Data() }
777+
JSONEncoderTests.test("testEncodingCustomData") { TestJSONEncoder().testEncodingCustomData() }
778+
JSONEncoderTests.test("testEncodingCustomDataEmpty") { TestJSONEncoder().testEncodingCustomDataEmpty() }
779+
JSONEncoderTests.test("testEncodingNonConformingFloats") { TestJSONEncoder().testEncodingNonConformingFloats() }
780+
JSONEncoderTests.test("testEncodingNonConformingFloatStrings") { TestJSONEncoder().testEncodingNonConformingFloatStrings() }
781+
JSONEncoderTests.test("testNestedContainerCodingPaths") { TestJSONEncoder().testNestedContainerCodingPaths() }
782+
JSONEncoderTests.test("testSuperEncoderCodingPaths") { TestJSONEncoder().testSuperEncoderCodingPaths() }
701783
runAllTests()
702784
#endif

0 commit comments

Comments
 (0)