diff --git a/Sources/XMLCoder/Encoder/XMLEncoder.swift b/Sources/XMLCoder/Encoder/XMLEncoder.swift index 7f60338d..ed9b199c 100644 --- a/Sources/XMLCoder/Encoder/XMLEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLEncoder.swift @@ -796,7 +796,15 @@ fileprivate struct _XMLUnkeyedEncodingContainer : UnkeyedEncodingContainer { public mutating func encode(_ value: T) throws { self.encoder.codingPath.append(_XMLKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } + let nodeEncodings = self.encoder.options.nodeEncodingStrategy.nodeEncodings( + forType: T.self, + with: self.encoder + ) + self.encoder.nodeEncodings.append(nodeEncodings) + defer { + let _ = self.encoder.nodeEncodings.removeLast() + self.encoder.codingPath.removeLast() + } self.container.add(try self.encoder.box(value)) } diff --git a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift new file mode 100644 index 00000000..a4b5e73d --- /dev/null +++ b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift @@ -0,0 +1,192 @@ +import XCTest +@testable import XMLCoder + +class NodeEncodingStrategyTests: XCTestCase { + fileprivate struct SingleContainer: Encodable { + let element: Element + + enum CodingKeys: String, CodingKey { + case element = "element" + } + } + + fileprivate struct KeyedContainer: Encodable { + let elements: [String: Element] + + enum CodingKeys: String, CodingKey { + case elements = "element" + } + } + + fileprivate struct UnkeyedContainer: Encodable { + let elements: [Element] + + enum CodingKeys: String, CodingKey { + case elements = "element" + } + } + + fileprivate struct Element: Encodable { + let key: String = "value" + + enum CodingKeys: CodingKey { + case key + } + + static func nodeEncoding(forKey codingKey: CodingKey) -> XMLEncoder.NodeEncoding { + return .attribute + } + } + + func testSingleContainer() { + let encoder = XMLEncoder() + encoder.outputFormatting = .prettyPrinted + + do { + let container = SingleContainer(element: Element()) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = +""" + + + value + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + + encoder.nodeEncodingStrategy = .custom { codableType, encoder in + guard let barType = codableType as? Element.Type else { + return { _ in return .default } + } + return barType.nodeEncoding(forKey:) + } + + do { + let container = SingleContainer(element: Element()) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = +""" + + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + } + + func testKeyedContainer() { + let encoder = XMLEncoder() + encoder.outputFormatting = .prettyPrinted + + do { + let container = KeyedContainer(elements: ["first": Element()]) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = + """ + + + + value + + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + + encoder.nodeEncodingStrategy = .custom { codableType, encoder in + guard let barType = codableType as? Element.Type else { + return { _ in return .default } + } + return barType.nodeEncoding(forKey:) + } + + do { + let container = KeyedContainer(elements: ["first": Element()]) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = + """ + + + + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + } + + func testUnkeyedContainer() { + let encoder = XMLEncoder() + encoder.outputFormatting = .prettyPrinted + + do { + let container = UnkeyedContainer(elements: [Element(), Element()]) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = +""" + + + value + + + value + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + + encoder.nodeEncodingStrategy = .custom { codableType, encoder in + guard let barType = codableType as? Element.Type else { + return { _ in return .default } + } + return barType.nodeEncoding(forKey:) + } + + do { + let container = UnkeyedContainer(elements: [Element(), Element()]) + let data = try encoder.encode(container, withRootKey: "container") + let xml = String(data: data, encoding: .utf8)! + + let expected = +""" + + + + +""" + XCTAssertEqual(xml, expected) + } catch { + XCTAssert(false, "failed to decode the example: \(error)") + } + } + + static var allTests = [ + ("testSingleContainer", testSingleContainer), + ("testKeyedContainer", testKeyedContainer), + ("testUnkeyedContainer", testUnkeyedContainer), + ] +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index ee8cddb0..3edb427b 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + BFE1C59121A4242300EA0458 /* NodeEncodingStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE1C58F21A4232100EA0458 /* NodeEncodingStrategyTests.swift */; }; OBJ_35 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; OBJ_41 /* XMLCoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* XMLCoderTests.swift */; }; OBJ_43 /* XMLCoder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "XMLParsing::XMLParsing::Product" /* XMLCoder.framework */; }; @@ -56,6 +57,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + BFE1C58F21A4232100EA0458 /* NodeEncodingStrategyTests.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = NodeEncodingStrategyTests.swift; sourceTree = ""; tabWidth = 4; }; D1BCEBCF21943CA6000B550F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D1BCEBD021943F09000B550F /* LinuxMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LinuxMain.swift; path = Tests/LinuxMain.swift; sourceTree = ""; }; D1BCEBD12194416E000B550F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -124,6 +126,7 @@ isa = PBXGroup; children = ( OBJ_25 /* XMLCoderTests.swift */, + BFE1C58F21A4232100EA0458 /* NodeEncodingStrategyTests.swift */, ); name = XMLCoderTests; path = Tests/XMLCoderTests; @@ -290,6 +293,7 @@ buildActionMask = 0; files = ( OBJ_41 /* XMLCoderTests.swift in Sources */, + BFE1C59121A4242300EA0458 /* NodeEncodingStrategyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };