From 099272ed7c3653d92628a4efdc1c81d743243598 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Fri, 21 Dec 2018 22:19:30 +0100 Subject: [PATCH] Fix a bug with throws on `Encodable` encoding nothing (#31) By encoding via `func encode(_:withRootKey:header:)` we always get an XML root node with label `rootKey`, so not encoding anything within `func encode(to:)` is fine in XML (unlike JSON). * Fixed a bug that would cause `XMLEncoder` to throw on `Encodable`s encoding nothing * Fix merge conflict * Fix indentation and xcodeproj file --- Sources/XMLCoder/Encoder/XMLEncoder.swift | 24 +++++----- Tests/XMLCoderTests/Minimal/EmptyTests.swift | 50 ++++++++++++++++++++ XMLCoder.xcodeproj/project.pbxproj | 6 +++ 3 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 Tests/XMLCoderTests/Minimal/EmptyTests.swift diff --git a/Sources/XMLCoder/Encoder/XMLEncoder.swift b/Sources/XMLCoder/Encoder/XMLEncoder.swift index 3bf36dbb..7b9f4f23 100644 --- a/Sources/XMLCoder/Encoder/XMLEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLEncoder.swift @@ -266,9 +266,9 @@ open class XMLEncoder { encoder.nodeEncodings.append(options.nodeEncodingStrategy.nodeEncodings(forType: T.self, with: encoder)) let topLevel = try encoder.box(value) - + let elementOrNone: _XMLElement? - + if let keyed = topLevel as? KeyedBox { elementOrNone = _XMLElement(key: rootKey, box: keyed) } else if let unkeyed = topLevel as? UnkeyedBox { @@ -276,7 +276,7 @@ open class XMLEncoder { } else { fatalError("Unrecognized top-level element.") } - + guard let element = elementOrNone else { throw EncodingError.invalidValue(value, EncodingError.Context( codingPath: [], @@ -379,7 +379,7 @@ class _XMLEncoder: Encoder { extension _XMLEncoder: SingleValueEncodingContainer { // MARK: - SingleValueEncodingContainer Methods - + func assertCanEncodeNewValue() { precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") } @@ -470,23 +470,23 @@ extension _XMLEncoder { func box() -> SimpleBox { return NullBox() } - + func box(_ value: Bool) -> SimpleBox { return BoolBox(value) } - + func box(_ value: Decimal) -> SimpleBox { return DecimalBox(value) } - + func box(_ value: T) -> SimpleBox { return IntBox(value) } - + func box(_ value: T) -> SimpleBox { return UIntBox(value) } - + func box(_ value: T) throws -> SimpleBox { guard value.isInfinite || value.isNaN else { return FloatBox(value) @@ -506,7 +506,7 @@ extension _XMLEncoder { func box(_ value: String) -> SimpleBox { return StringBox(value) } - + func box(_ value: Date) throws -> Box { switch options.dateEncodingStrategy { case .deferredToDate: @@ -550,8 +550,8 @@ extension _XMLEncoder { func box(_ value: URL) -> SimpleBox { return URLBox(value) } - - func box(_ value: T) throws -> Box { + + internal func box(_ value: T) throws -> Box { if T.self == Date.self || T.self == NSDate.self { return try box(value as! Date) } else if T.self == Data.self || T.self == NSData.self { diff --git a/Tests/XMLCoderTests/Minimal/EmptyTests.swift b/Tests/XMLCoderTests/Minimal/EmptyTests.swift new file mode 100644 index 00000000..1065b6da --- /dev/null +++ b/Tests/XMLCoderTests/Minimal/EmptyTests.swift @@ -0,0 +1,50 @@ +// +// EmptyTests.swift +// XMLCoderTests +// +// Created by Vincent Esche on 12/19/18. +// + +import XCTest +@testable import XMLCoder + +class EmptyTests: XCTestCase { + struct Container: Codable, Equatable { + // empty + + func encode(to encoder: Encoder) throws { + // do nothing + } + } + + func testAttribute() throws { + let decoder = XMLDecoder() + let encoder = XMLEncoder() + + encoder.nodeEncodingStrategy = .custom { codableType, _ in + return { _ in .attribute } + } + + XCTAssertThrowsError(try decoder.decode(Container.self, from: Data())) + + let encoded = try encoder.encode(Container(), withRootKey: "container") + XCTAssertEqual(String(data: encoded, encoding: .utf8)!, "") + } + + func testElement() throws { + let decoder = XMLDecoder() + let encoder = XMLEncoder() + + encoder.outputFormatting = [.prettyPrinted] + + XCTAssertThrowsError(try decoder.decode(Container.self, from: Data())) + + let encoded = try encoder.encode(Container(), withRootKey: "container") + XCTAssertEqual(String(data: encoded, encoding: .utf8)!, "") + } + + static var allTests = [ + ("testAttribute", testAttribute), + ("testElement", testElement), + ] +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 4a1c79fa..c76b16d6 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ BF63EF0621CD7A74001D38C5 /* URLBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0521CD7A74001D38C5 /* URLBox.swift */; }; BF63EF0821CD7AF8001D38C5 /* URLBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */; }; BF63EF0A21CD7C1A001D38C5 /* URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0921CD7C1A001D38C5 /* URLTests.swift */; }; + BF63EF0C21CD7F28001D38C5 /* EmptyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0B21CD7F28001D38C5 /* EmptyTests.swift */; }; BF9457A821CBB498005ACFDE /* NullBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF94579E21CBB497005ACFDE /* NullBox.swift */; }; BF9457A921CBB498005ACFDE /* KeyedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF94579F21CBB497005ACFDE /* KeyedBox.swift */; }; BF9457AA21CBB498005ACFDE /* UnkeyedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457A021CBB497005ACFDE /* UnkeyedBox.swift */; }; @@ -113,6 +114,7 @@ BF63EF0521CD7A74001D38C5 /* URLBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBox.swift; sourceTree = ""; }; BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBoxTests.swift; sourceTree = ""; }; BF63EF0921CD7C1A001D38C5 /* URLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLTests.swift; sourceTree = ""; }; + BF63EF0B21CD7F28001D38C5 /* EmptyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTests.swift; sourceTree = ""; }; BF94579E21CBB497005ACFDE /* NullBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NullBox.swift; sourceTree = ""; }; BF94579F21CBB497005ACFDE /* KeyedBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedBox.swift; sourceTree = ""; }; BF9457A021CBB497005ACFDE /* UnkeyedBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnkeyedBox.swift; sourceTree = ""; }; @@ -263,6 +265,7 @@ BF9457E121CBB6BC005ACFDE /* Minimal */ = { isa = PBXGroup; children = ( + BF63EF0B21CD7F28001D38C5 /* EmptyTests.swift */, BF9457E221CBB6BC005ACFDE /* BoolTests.swift */, BF9457E321CBB6BC005ACFDE /* IntTests.swift */, BF9457E421CBB6BC005ACFDE /* NullTests.swift */, @@ -340,7 +343,9 @@ OBJ_39 /* build */, OBJ_40 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; OBJ_7 /* Sources */ = { isa = PBXGroup; @@ -523,6 +528,7 @@ OBJ_83 /* CDTest.swift in Sources */, OBJ_85 /* NodeEncodingStrategyTests.swift in Sources */, OBJ_86 /* NoteTest.swift in Sources */, + BF63EF0C21CD7F28001D38C5 /* EmptyTests.swift in Sources */, BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */, BF9457EE21CBB6BC005ACFDE /* IntTests.swift in Sources */, OBJ_87 /* PlantCatalog.swift in Sources */,