Skip to content

Commit 3283efb

Browse files
authored
Merge pull request #10261 from itaiferber/swift-4.0-branch
[4.0] Allow SingleValueContainers to decode collections
2 parents 92ae28d + 0ba863e commit 3283efb

File tree

4 files changed

+215
-42
lines changed

4 files changed

+215
-42
lines changed

stdlib/public/SDK/Foundation/JSONEncoder.swift

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

964964
func singleValueContainer() throws -> SingleValueDecodingContainer {
965-
guard !(self.storage.topContainer is [String : Any]) else {
966-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
967-
DecodingError.Context(codingPath: self.codingPath,
968-
debugDescription: "Cannot get single value decoding container -- found keyed container instead."))
969-
}
970-
971-
guard !(self.storage.topContainer is [Any]) else {
972-
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
973-
DecodingError.Context(codingPath: self.codingPath,
974-
debugDescription: "Cannot get single value decoding container -- found unkeyed container instead."))
975-
}
976-
977965
return self
978966
}
979967
}

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: 88 additions & 4 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
@@ -326,7 +343,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
326343
encoder.nonConformingFloatEncodingStrategy = nonConformingFloatEncodingStrategy
327344
payload = try encoder.encode(value)
328345
} catch {
329-
expectUnreachable("Failed to encode \(T.self) to JSON.")
346+
expectUnreachable("Failed to encode \(T.self) to JSON: \(error)")
330347
}
331348

332349
if let expectedJSON = json {
@@ -341,7 +358,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
341358
let decoded = try decoder.decode(T.self, from: payload)
342359
expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.")
343360
} catch {
344-
expectUnreachable("Failed to decode \(T.self) from JSON.")
361+
expectUnreachable("Failed to decode \(T.self) from JSON: \(error)")
345362
}
346363
}
347364
}
@@ -429,7 +446,7 @@ fileprivate enum Switch : Codable {
429446
}
430447

431448
/// A simple timestamp type that encodes as a single Double value.
432-
fileprivate struct Timestamp : Codable {
449+
fileprivate struct Timestamp : Codable, Equatable {
433450
let value: Double
434451

435452
init(_ value: Double) {
@@ -445,10 +462,14 @@ fileprivate struct Timestamp : Codable {
445462
var container = encoder.singleValueContainer()
446463
try container.encode(self.value)
447464
}
465+
466+
static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool {
467+
return lhs.value == rhs.value
468+
}
448469
}
449470

450471
/// A simple referential counter type that encodes as a single Int value.
451-
fileprivate final class Counter : Codable {
472+
fileprivate final class Counter : Codable, Equatable {
452473
var count: Int = 0
453474

454475
init() {}
@@ -462,6 +483,10 @@ fileprivate final class Counter : Codable {
462483
var container = encoder.singleValueContainer()
463484
try container.encode(self.count)
464485
}
486+
487+
static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool {
488+
return lhs === rhs || lhs.count == rhs.count
489+
}
465490
}
466491

467492
// MARK: - Structured Types
@@ -612,6 +637,62 @@ fileprivate struct DoubleNaNPlaceholder : Codable, Equatable {
612637
}
613638
}
614639

640+
/// A type which encodes as an array directly through a single value container.
641+
struct Numbers : Codable, Equatable {
642+
let values = [4, 8, 15, 16, 23, 42]
643+
644+
init() {}
645+
646+
init(from decoder: Decoder) throws {
647+
let container = try decoder.singleValueContainer()
648+
let decodedValues = try container.decode([Int].self)
649+
guard decodedValues == values else {
650+
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!"))
651+
}
652+
}
653+
654+
func encode(to encoder: Encoder) throws {
655+
var container = encoder.singleValueContainer()
656+
try container.encode(values)
657+
}
658+
659+
static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool {
660+
return lhs.values == rhs.values
661+
}
662+
663+
static var testValue: Numbers {
664+
return Numbers()
665+
}
666+
}
667+
668+
/// A type which encodes as a dictionary directly through a single value container.
669+
fileprivate final class Mapping : Codable, Equatable {
670+
let values: [String : URL]
671+
672+
init(values: [String : URL]) {
673+
self.values = values
674+
}
675+
676+
init(from decoder: Decoder) throws {
677+
let container = try decoder.singleValueContainer()
678+
values = try container.decode([String : URL].self)
679+
}
680+
681+
func encode(to encoder: Encoder) throws {
682+
var container = encoder.singleValueContainer()
683+
try container.encode(values)
684+
}
685+
686+
static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool {
687+
return lhs === rhs || lhs.values == rhs.values
688+
}
689+
690+
static var testValue: Mapping {
691+
return Mapping(values: ["Apple": URL(string: "http://apple.com")!,
692+
"localhost": URL(string: "http://127.0.0.1")!])
693+
}
694+
}
695+
615696
struct NestedContainersTestType : Encodable {
616697
let testSuperEncoder: Bool
617698

@@ -713,6 +794,9 @@ JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder
713794
JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() }
714795
JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() }
715796
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
797+
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleStruct") { TestJSONEncoder().testEncodingTopLevelStructuredSingleStruct() }
798+
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleClass") { TestJSONEncoder().testEncodingTopLevelStructuredSingleClass() }
799+
JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncoder().testEncodingTopLevelDeepStructuredType()}
716800
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
717801
JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncoder().testEncodingTopLevelDeepStructuredType()}
718802
JSONEncoderTests.test("testEncodingOutputFormattingDefault") { TestJSONEncoder().testEncodingOutputFormattingDefault() }

0 commit comments

Comments
 (0)