diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index 066b68f4..499f4450 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -12,18 +12,29 @@ class XMLStackParser: NSObject { var root: XMLCoderElement? private var stack: [XMLCoderElement] = [] - static func parse(with data: Data, - errorContextLength length: UInt) throws -> KeyedBox { + static func parse( + with data: Data, + errorContextLength length: UInt, + shouldProcessNamespaces: Bool + ) throws -> KeyedBox { let parser = XMLStackParser() - let node = try parser.parse(with: data, errorContextLength: length) + let node = try parser.parse( + with: data, + errorContextLength: length, + shouldProcessNamespaces: shouldProcessNamespaces + ) return node.flatten() } - func parse(with data: Data, - errorContextLength: UInt) throws -> XMLCoderElement { + func parse( + with data: Data, + errorContextLength: UInt, + shouldProcessNamespaces: Bool + ) throws -> XMLCoderElement { let xmlParser = XMLParser(data: data) + xmlParser.shouldProcessNamespaces = shouldProcessNamespaces xmlParser.delegate = self guard !xmlParser.parse(), root == nil else { @@ -92,8 +103,8 @@ extension XMLStackParser: XMLParserDelegate { func parser(_: XMLParser, didStartElement elementName: String, - namespaceURI _: String?, - qualifiedName _: String?, + namespaceURI: String?, + qualifiedName: String?, attributes attributeDict: [String: String] = [:]) { let element = XMLCoderElement(key: elementName, attributes: attributeDict) stack.append(element) diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index 6b941f69..653c6c54 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -245,6 +245,11 @@ open class XMLDecoder { /// span more than a few lines. open var errorContextLength: UInt = 0 + /** A boolean value that determines whether the parser reports the + namespaces and qualified names of elements. The default value is `false`. + */ + open var shouldProcessNamespaces: Bool = false + /// Options set on the top-level encoder to pass down the decoding hierarchy. struct Options { let dateDecodingStrategy: DateDecodingStrategy @@ -283,7 +288,8 @@ open class XMLDecoder { ) throws -> T { let topLevel: Box = try XMLStackParser.parse( with: data, - errorContextLength: errorContextLength + errorContextLength: errorContextLength, + shouldProcessNamespaces: shouldProcessNamespaces ) let decoder = XMLDecoderImplementation(referencing: topLevel, options: options) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 958f3d1e..b02d4791 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -21,8 +21,11 @@ class XMLStackParserTests: XCTestCase { """ let xmlData = xmlString.data(using: .utf8)! - let root: XMLCoderElement? = try parser.parse(with: xmlData, - errorContextLength: 0) + let root: XMLCoderElement? = try parser.parse( + with: xmlData, + errorContextLength: 0, + shouldProcessNamespaces: false + ) let expected = XMLCoderElement( key: "container", @@ -46,7 +49,10 @@ class XMLStackParserTests: XCTestCase { let xmlString = "lorem ipsum" let xmlData = xmlString.data(using: .utf8)! - XCTAssertThrowsError(try parser.parse(with: xmlData, - errorContextLength: 0)) + XCTAssertThrowsError(try parser.parse( + with: xmlData, + errorContextLength: 0, + shouldProcessNamespaces: false + )) } } diff --git a/Tests/XMLCoderTests/NamespaceTest.swift b/Tests/XMLCoderTests/NamespaceTest.swift new file mode 100644 index 00000000..f0360e9d --- /dev/null +++ b/Tests/XMLCoderTests/NamespaceTest.swift @@ -0,0 +1,38 @@ +// +// NamespaceTest.swift +// XMLCoder +// +// Created by Max Desiatov on 27/02/2019. +// + +import Foundation +import XCTest +@testable import XMLCoder + +private let xmlData = """ + + + Apples + Bananas + + +""".data(using: .utf8)! + +private struct Table: Codable, Equatable { + struct TR: Codable, Equatable { + let td: [String] + } + + let tr: TR +} + +class NameSpaceTest: XCTestCase { + func testDecoder() throws { + let decoder = XMLDecoder() + decoder.shouldProcessNamespaces = true + + let decoded = try decoder.decode(Table.self, from: xmlData) + + XCTAssertEqual(decoded, Table(tr: .init(td: ["Apples", "Bananas"]))) + } +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 33627d76..4a8f642d 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ D14D8A8621F1D6B300B0D31A /* SingleChildTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */; }; D162674321F9B2AF0056D1D8 /* OptionalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D162674121F9B2850056D1D8 /* OptionalTests.swift */; }; D1CB1EF521EA9599009CAF02 /* RJITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* RJITest.swift */; }; + D1CFC8242226B13F00B03222 /* NamespaceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CFC8222226AFB400B03222 /* NamespaceTest.swift */; }; D1E0C85321D8E65E0042A261 /* ErrorContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85121D8E6540042A261 /* ErrorContextTest.swift */; }; D1E0C85521D91EBF0042A261 /* Metatypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85421D91EBF0042A261 /* Metatypes.swift */; }; D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1FC040421C7EF8200065B43 /* RJISample.swift */; }; @@ -198,6 +199,7 @@ D11979B721F5EA5400A9C574 /* XMLCoder.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = XMLCoder.podspec; sourceTree = ""; }; D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleChildTests.swift; sourceTree = ""; }; D162674121F9B2850056D1D8 /* OptionalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalTests.swift; sourceTree = ""; }; + D1CFC8222226AFB400B03222 /* NamespaceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NamespaceTest.swift; sourceTree = ""; }; D1E0C85121D8E6540042A261 /* ErrorContextTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorContextTest.swift; sourceTree = ""; }; D1E0C85421D91EBF0042A261 /* Metatypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metatypes.swift; sourceTree = ""; }; D1FC040421C7EF8200065B43 /* RJISample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RJISample.swift; sourceTree = ""; }; @@ -384,6 +386,7 @@ B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */, A61DCCD621DF8DB300C0A19D /* ClassTests.swift */, D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */, + D1CFC8222226AFB400B03222 /* NamespaceTest.swift */, ); name = XMLCoderTests; path = Tests/XMLCoderTests; @@ -625,6 +628,7 @@ D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */, BF63EF0A21CD7C1A001D38C5 /* URLTests.swift in Sources */, BF9457CE21CBB516005ACFDE /* StringBoxTests.swift in Sources */, + D1CFC8242226B13F00B03222 /* NamespaceTest.swift in Sources */, BF9457D021CBB516005ACFDE /* UIntBoxTests.swift in Sources */, OBJ_80 /* BooksTest.swift in Sources */, OBJ_81 /* BreakfastTest.swift in Sources */,