Skip to content

Allow SingleValueContainers to decode collections #10249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions stdlib/public/SDK/Foundation/JSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -952,18 +952,6 @@ fileprivate class _JSONDecoder : Decoder {
}

func singleValueContainer() throws -> SingleValueDecodingContainer {
guard !(self.storage.topContainer is [String : Any]) else {
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get single value decoding container -- found keyed container instead."))
}

guard !(self.storage.topContainer is [Any]) else {
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get single value decoding container -- found unkeyed container instead."))
}

return self
}
}
Expand Down
12 changes: 0 additions & 12 deletions stdlib/public/SDK/Foundation/PlistEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -733,18 +733,6 @@ fileprivate class _PlistDecoder : Decoder {
}

func singleValueContainer() throws -> SingleValueDecodingContainer {
guard !(self.storage.topContainer is [String : Any]) else {
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get single value decoding container -- found keyed container instead."))
}

guard !(self.storage.topContainer is [Any]) else {
throw DecodingError.typeMismatch(SingleValueDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get single value decoding container -- found unkeyed container instead."))
}

return self
}
}
Expand Down
134 changes: 108 additions & 26 deletions test/stdlib/TestJSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@ class TestJSONEncoder : TestJSONEncoderSuper {
func testEncodingTopLevelSingleValueEnum() {
_testEncodeFailure(of: Switch.off)
_testEncodeFailure(of: Switch.on)

_testRoundTrip(of: TopLevelWrapper(Switch.off))
_testRoundTrip(of: TopLevelWrapper(Switch.on))
}

func testEncodingTopLevelSingleValueStruct() {
_testEncodeFailure(of: Timestamp(3141592653))
_testRoundTrip(of: TopLevelWrapper(Timestamp(3141592653)))
}

func testEncodingTopLevelSingleValueClass() {
_testEncodeFailure(of: Counter())
_testRoundTrip(of: TopLevelWrapper(Counter()))
}

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

func testEncodingTopLevelStructuredSingleStruct() {
// Numbers is a struct which encodes as an array through a single value container.
let numbers = Numbers.testValue
_testRoundTrip(of: numbers)
}

func testEncodingTopLevelStructuredSingleClass() {
// Mapping is a class which encodes as a dictionary through a single value container.
let mapping = Mapping.testValue
_testRoundTrip(of: mapping)
}

func testEncodingTopLevelDeepStructuredType() {
// Company is a type with fields which are Codable themselves.
let company = Company.testValue
Expand Down Expand Up @@ -295,7 +312,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
encoder.nonConformingFloatEncodingStrategy = nonConformingFloatEncodingStrategy
payload = try encoder.encode(value)
} catch {
expectUnreachable("Failed to encode \(T.self) to JSON.")
expectUnreachable("Failed to encode \(T.self) to JSON: \(error)")
}

if let expectedJSON = json {
Expand All @@ -310,7 +327,7 @@ class TestJSONEncoder : TestJSONEncoderSuper {
let decoded = try decoder.decode(T.self, from: payload)
expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.")
} catch {
expectUnreachable("Failed to decode \(T.self) from JSON.")
expectUnreachable("Failed to decode \(T.self) from JSON: \(error)")
}
}
}
Expand Down Expand Up @@ -398,7 +415,7 @@ fileprivate enum Switch : Codable {
}

/// A simple timestamp type that encodes as a single Double value.
fileprivate struct Timestamp : Codable {
fileprivate struct Timestamp : Codable, Equatable {
let value: Double

init(_ value: Double) {
Expand All @@ -414,10 +431,14 @@ fileprivate struct Timestamp : Codable {
var container = encoder.singleValueContainer()
try container.encode(self.value)
}

static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool {
return lhs.value == rhs.value
}
}

/// A simple referential counter type that encodes as a single Int value.
fileprivate final class Counter : Codable {
fileprivate final class Counter : Codable, Equatable {
var count: Int = 0

init() {}
Expand All @@ -431,6 +452,10 @@ fileprivate final class Counter : Codable {
var container = encoder.singleValueContainer()
try container.encode(self.count)
}

static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool {
return lhs === rhs || lhs.count == rhs.count
}
}

// MARK: - Structured Types
Expand Down Expand Up @@ -581,6 +606,62 @@ fileprivate struct DoubleNaNPlaceholder : Codable, Equatable {
}
}

/// A type which encodes as an array directly through a single value container.
struct Numbers : Codable, Equatable {
let values = [4, 8, 15, 16, 23, 42]

init() {}

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedValues = try container.decode([Int].self)
guard decodedValues == values else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!"))
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(values)
}

static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool {
return lhs.values == rhs.values
}

static var testValue: Numbers {
return Numbers()
}
}

/// A type which encodes as a dictionary directly through a single value container.
fileprivate final class Mapping : Codable, Equatable {
let values: [String : URL]

init(values: [String : URL]) {
self.values = values
}

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
values = try container.decode([String : URL].self)
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(values)
}

static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool {
return lhs === rhs || lhs.values == rhs.values
}

static var testValue: Mapping {
return Mapping(values: ["Apple": URL(string: "http://apple.com")!,
"localhost": URL(string: "http://127.0.0.1")!])
}
}

struct NestedContainersTestType : Encodable {
let testSuperEncoder: Bool

Expand Down Expand Up @@ -675,28 +756,29 @@ struct NestedContainersTestType : Encodable {

#if !FOUNDATION_XCTEST
var JSONEncoderTests = TestSuite("TestJSONEncoder")
JSONEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestJSONEncoder().testEncodingTopLevelEmptyStruct() }
JSONEncoderTests.test("testEncodingTopLevelEmptyClass") { TestJSONEncoder().testEncodingTopLevelEmptyClass() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestJSONEncoder().testEncodingTopLevelSingleValueEnum() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder().testEncodingTopLevelSingleValueStruct() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() }
JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() }
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
JSONEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestJSONEncoder().testEncodingTopLevelEmptyStruct() }
JSONEncoderTests.test("testEncodingTopLevelEmptyClass") { TestJSONEncoder().testEncodingTopLevelEmptyClass() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestJSONEncoder().testEncodingTopLevelSingleValueEnum() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder().testEncodingTopLevelSingleValueStruct() }
JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() }
JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() }
JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() }
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleStruct") { TestJSONEncoder().testEncodingTopLevelStructuredSingleStruct() }
JSONEncoderTests.test("testEncodingTopLevelStructuredSingleClass") { TestJSONEncoder().testEncodingTopLevelStructuredSingleClass() }
JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncoder().testEncodingTopLevelDeepStructuredType()}
JSONEncoderTests.test("testEncodingDate") { TestJSONEncoder().testEncodingDate() }
JSONEncoderTests.test("testEncodingDateSecondsSince1970") { TestJSONEncoder().testEncodingDateSecondsSince1970() }
JSONEncoderTests.test("testEncodingDateMillisecondsSince1970") { TestJSONEncoder().testEncodingDateMillisecondsSince1970() }
JSONEncoderTests.test("testEncodingDateISO8601") { TestJSONEncoder().testEncodingDateISO8601() }
JSONEncoderTests.test("testEncodingDateFormatted") { TestJSONEncoder().testEncodingDateFormatted() }
JSONEncoderTests.test("testEncodingDateCustom") { TestJSONEncoder().testEncodingDateCustom() }
JSONEncoderTests.test("testEncodingDateCustomEmpty") { TestJSONEncoder().testEncodingDateCustomEmpty() }
JSONEncoderTests.test("testEncodingBase64Data") { TestJSONEncoder().testEncodingBase64Data() }
JSONEncoderTests.test("testEncodingCustomData") { TestJSONEncoder().testEncodingCustomData() }
JSONEncoderTests.test("testEncodingCustomDataEmpty") { TestJSONEncoder().testEncodingCustomDataEmpty() }
JSONEncoderTests.test("testEncodingNonConformingFloats") { TestJSONEncoder().testEncodingNonConformingFloats() }
JSONEncoderTests.test("testEncodingNonConformingFloatStrings") { TestJSONEncoder().testEncodingNonConformingFloatStrings() }
JSONEncoderTests.test("testNestedContainerCodingPaths") { TestJSONEncoder().testNestedContainerCodingPaths() }
JSONEncoderTests.test("testSuperEncoderCodingPaths") { TestJSONEncoder().testSuperEncoderCodingPaths() }
JSONEncoderTests.test("testEncodingDate") { TestJSONEncoder().testEncodingDate() }
JSONEncoderTests.test("testEncodingDateSecondsSince1970") { TestJSONEncoder().testEncodingDateSecondsSince1970() }
JSONEncoderTests.test("testEncodingDateMillisecondsSince1970") { TestJSONEncoder().testEncodingDateMillisecondsSince1970() }
JSONEncoderTests.test("testEncodingDateISO8601") { TestJSONEncoder().testEncodingDateISO8601() }
JSONEncoderTests.test("testEncodingDateFormatted") { TestJSONEncoder().testEncodingDateFormatted() }
JSONEncoderTests.test("testEncodingDateCustom") { TestJSONEncoder().testEncodingDateCustom() }
JSONEncoderTests.test("testEncodingDateCustomEmpty") { TestJSONEncoder().testEncodingDateCustomEmpty() }
JSONEncoderTests.test("testEncodingBase64Data") { TestJSONEncoder().testEncodingBase64Data() }
JSONEncoderTests.test("testEncodingCustomData") { TestJSONEncoder().testEncodingCustomData() }
JSONEncoderTests.test("testEncodingCustomDataEmpty") { TestJSONEncoder().testEncodingCustomDataEmpty() }
JSONEncoderTests.test("testEncodingNonConformingFloats") { TestJSONEncoder().testEncodingNonConformingFloats() }
JSONEncoderTests.test("testEncodingNonConformingFloatStrings") { TestJSONEncoder().testEncodingNonConformingFloatStrings() }
JSONEncoderTests.test("testNestedContainerCodingPaths") { TestJSONEncoder().testNestedContainerCodingPaths() }
JSONEncoderTests.test("testSuperEncoderCodingPaths") { TestJSONEncoder().testSuperEncoderCodingPaths() }
runAllTests()
#endif
Loading