From 132b8502c0336bcf21af2adcc85c93f4787d74aa Mon Sep 17 00:00:00 2001 From: John Woo Date: Tue, 27 Jul 2021 11:49:28 -0700 Subject: [PATCH 01/12] chore: filter out pure whitespace with nested elements version 2 --- .../Auxiliaries/String+Extensions.swift | 6 +++++ .../Auxiliaries/XMLCoderElement.swift | 7 ++++++ .../XMLCoder/Auxiliaries/XMLStackParser.swift | 25 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Sources/XMLCoder/Auxiliaries/String+Extensions.swift b/Sources/XMLCoder/Auxiliaries/String+Extensions.swift index f7a71091..72e63c3d 100644 --- a/Sources/XMLCoder/Auxiliaries/String+Extensions.swift +++ b/Sources/XMLCoder/Auxiliaries/String+Extensions.swift @@ -44,3 +44,9 @@ extension StringProtocol { self = lowercasingFirstLetter() } } + +extension String { + func isAllWhitespace() -> Bool { + return self.trimmingCharacters(in: .whitespacesAndNewlines) == "" + } +} diff --git a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift index db3a12f3..c3a96673 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift @@ -392,3 +392,10 @@ extension XMLCoderElement { } } } + +extension XMLCoderElement { + func isWhitespaceWithNoElements() -> Bool { + let stringValueIsWhitespaceOrNil = stringValue?.isAllWhitespace() ?? true + return self.key == "" && stringValueIsWhitespaceOrNil && self.elements.isEmpty + } +} diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index 41833a71..0ebd692b 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -141,8 +141,10 @@ extension XMLStackParser: XMLParserDelegate { return } + let updatedElement = elementWithFilteredElements(element: element) + withCurrentElement { currentElement in - currentElement.append(element: element, forKey: element.key) + currentElement.append(element: updatedElement, forKey: updatedElement.key) } if stack.isEmpty { @@ -150,6 +152,27 @@ extension XMLStackParser: XMLParserDelegate { } } + func elementWithFilteredElements(element: XMLCoderElement) -> XMLCoderElement { + var hasWhitespaceElements: Bool = false + var hasNonWhitespaceElements: Bool = false + var filteredElements: [XMLCoderElement] = [] + for ele in element.elements { + if ele.isWhitespaceWithNoElements() { + hasWhitespaceElements = true + } else { + hasNonWhitespaceElements = true + filteredElements.append(ele) + } + } + let updatedElement: XMLCoderElement + if hasWhitespaceElements && hasNonWhitespaceElements { + updatedElement = XMLCoderElement(key: element.key, elements: filteredElements, attributes: element.attributes) + } else { + updatedElement = element + } + return updatedElement + } + func parser(_: XMLParser, foundCharacters string: String) { let processedString = process(string: string) guard processedString.count > 0, string.count != 0 else { From 6c970162c5606b60bf177880c7a4e279f459b9cb Mon Sep 17 00:00:00 2001 From: John Woo Date: Wed, 28 Jul 2021 18:18:45 -0700 Subject: [PATCH 02/12] Adding flag, update unit tests, fix bug --- .../XMLCoder/Auxiliaries/XMLStackParser.swift | 14 ++- Sources/XMLCoder/Decoder/XMLDecoder.swift | 12 ++- .../Auxiliary/String+ExtensionsTests.swift | 35 ++++++ .../Auxiliary/XMLElementTests.swift | 15 +++ .../Auxiliary/XMLStackParserTests.swift | 102 ++++++++++++++++++ 5 files changed, 171 insertions(+), 7 deletions(-) diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index 0ebd692b..c40f427e 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -15,9 +15,11 @@ class XMLStackParser: NSObject { var root: XMLCoderElement? private var stack: [XMLCoderElement] = [] private let trimValueWhitespaces: Bool + private let removeWhitespaceElements: Bool - init(trimValueWhitespaces: Bool = true) { + init(trimValueWhitespaces: Bool = true, removeWhitespaceElements: Bool = false) { self.trimValueWhitespaces = trimValueWhitespaces + self.removeWhitespaceElements = removeWhitespaceElements super.init() } @@ -25,9 +27,11 @@ class XMLStackParser: NSObject { with data: Data, errorContextLength length: UInt, shouldProcessNamespaces: Bool, - trimValueWhitespaces: Bool + trimValueWhitespaces: Bool, + removeWhitespaceElements: Bool ) throws -> Box { - let parser = XMLStackParser(trimValueWhitespaces: trimValueWhitespaces) + let parser = XMLStackParser(trimValueWhitespaces: trimValueWhitespaces, + removeWhitespaceElements: removeWhitespaceElements) let node = try parser.parse( with: data, @@ -141,14 +145,14 @@ extension XMLStackParser: XMLParserDelegate { return } - let updatedElement = elementWithFilteredElements(element: element) + let updatedElement = removeWhitespaceElements ? elementWithFilteredElements(element: element) : element withCurrentElement { currentElement in currentElement.append(element: updatedElement, forKey: updatedElement.key) } if stack.isEmpty { - root = element + root = updatedElement } } diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index 678752f8..4f479d0d 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -303,6 +303,12 @@ open class XMLDecoder { */ open var trimValueWhitespaces: Bool + /** A boolean value that determines whether to remove pure whitespace elements + that have sibling elements that aren't pure whitespace. The default value + is `false`. + */ + open var removeWhitespaceElements: Bool + /// Options set on the top-level encoder to pass down the decoding hierarchy. struct Options { let dateDecodingStrategy: DateDecodingStrategy @@ -328,8 +334,9 @@ open class XMLDecoder { // MARK: - Constructing a XML Decoder /// Initializes `self` with default strategies. - public init(trimValueWhitespaces: Bool = true) { + public init(trimValueWhitespaces: Bool = true, removeWhitespaceElements: Bool = false) { self.trimValueWhitespaces = trimValueWhitespaces + self.removeWhitespaceElements = removeWhitespaceElements } // MARK: - Decoding Values @@ -349,7 +356,8 @@ open class XMLDecoder { with: data, errorContextLength: errorContextLength, shouldProcessNamespaces: shouldProcessNamespaces, - trimValueWhitespaces: trimValueWhitespaces + trimValueWhitespaces: trimValueWhitespaces, + removeWhitespaceElements: removeWhitespaceElements ) let decoder = XMLDecoderImplementation( diff --git a/Tests/XMLCoderTests/Auxiliary/String+ExtensionsTests.swift b/Tests/XMLCoderTests/Auxiliary/String+ExtensionsTests.swift index efb593f7..19a3ef67 100644 --- a/Tests/XMLCoderTests/Auxiliary/String+ExtensionsTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/String+ExtensionsTests.swift @@ -41,4 +41,39 @@ class StringExtensionsTests: XCTestCase { } XCTAssertEqual(expected, mutated) } + + func testIsAllWhitespace() { + let testString1 = "" + let testString2 = " " + + let testString3 = "\n" + let testString4 = "\n " + let testString5 = " \n " + let testString6 = " \n" + + let testString7 = "\r" + let testString8 = "\r " + let testString9 = " \r " + let testString10 = " \r" + + let testString11 = "\r\n" + let testString12 = "\r\n " + let testString13 = " \r\n " + let testString14 = " \r\n" + + XCTAssert(testString1.isAllWhitespace()) + XCTAssert(testString2.isAllWhitespace()) + XCTAssert(testString3.isAllWhitespace()) + XCTAssert(testString4.isAllWhitespace()) + XCTAssert(testString5.isAllWhitespace()) + XCTAssert(testString6.isAllWhitespace()) + XCTAssert(testString7.isAllWhitespace()) + XCTAssert(testString8.isAllWhitespace()) + XCTAssert(testString9.isAllWhitespace()) + XCTAssert(testString10.isAllWhitespace()) + XCTAssert(testString11.isAllWhitespace()) + XCTAssert(testString12.isAllWhitespace()) + XCTAssert(testString13.isAllWhitespace()) + XCTAssert(testString14.isAllWhitespace()) + } } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift index 97d5f70a..fb828246 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift @@ -49,4 +49,19 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(keyed.elements, [element]) XCTAssertEqual(keyed.attributes, []) } + + func testWhitespaceWithNoElements_keyed() { + let keyed = XMLCoderElement(key: "foo", isStringBoxCDATA: false, box: StringBox("bar")) + XCTAssertFalse(keyed.isWhitespaceWithNoElements()) + } + + func testWhitespaceWithNoElements_whitespace() { + let whitespaceElement1 = XMLCoderElement(stringValue: "\n ") + let whitespaceElement2 = XMLCoderElement(stringValue: "\n") + let whitespaceElement3 = XMLCoderElement(stringValue: " ") + + XCTAssert(whitespaceElement1.isWhitespaceWithNoElements()) + XCTAssert(whitespaceElement2.isWhitespaceWithNoElements()) + XCTAssert(whitespaceElement3.isWhitespaceWithNoElements()) + } } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index deedec91..e094e612 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -56,4 +56,106 @@ class XMLStackParserTests: XCTestCase { shouldProcessNamespaces: false )) } + + func testNestedMembers_removeWhitespaceElements() throws { + let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: true) + let xmlData = + """ + + + + foo + bar + + + baz + qux + + + + """.data(using: .utf8)! + let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + + XCTAssertEqual(root.elements[0].key, "nestedStringList") + + XCTAssertEqual(root.elements[0].elements[0].key, "member") + XCTAssertEqual(root.elements[0].elements[0].elements[0].key, "member") + XCTAssertEqual(root.elements[0].elements[0].elements[0].elements[0].stringValue, "foo") + XCTAssertEqual(root.elements[0].elements[0].elements[1].elements[0].stringValue, "bar") + + XCTAssertEqual(root.elements[0].elements[1].key, "member") + XCTAssertEqual(root.elements[0].elements[1].elements[0].key, "member") + XCTAssertEqual(root.elements[0].elements[1].elements[0].elements[0].stringValue, "baz") + XCTAssertEqual(root.elements[0].elements[1].elements[1].elements[0].stringValue, "qux") + } + + func testNestedMembers() throws { + let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: false) + let xmlData = + """ + + + + foo + bar + + + baz + qux + + + + """.data(using: .utf8)! + let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + + XCTAssertEqual(root.elements[0].key, "") + XCTAssertEqual(root.elements[0].stringValue, "\n ") + + XCTAssertEqual(root.elements[1].key, "nestedStringList") + XCTAssertEqual(root.elements[1].elements[0].key, "") + XCTAssertEqual(root.elements[1].elements[0].stringValue, "\n ") + XCTAssertEqual(root.elements[1].elements[1].key, "member") + XCTAssertEqual(root.elements[1].elements[1].elements[0].stringValue, "\n ") + + XCTAssertEqual(root.elements[1].elements[1].elements[1].key, "member") + XCTAssertEqual(root.elements[1].elements[1].elements[1].elements[0].stringValue, "foo") + XCTAssertEqual(root.elements[1].elements[1].elements[3].key, "member") + XCTAssertEqual(root.elements[1].elements[1].elements[3].elements[0].stringValue, "bar") + + XCTAssertEqual(root.elements[1].elements[3].elements[1].key, "member") + XCTAssertEqual(root.elements[1].elements[3].elements[1].elements[0].stringValue, "baz") + XCTAssertEqual(root.elements[1].elements[3].elements[3].key, "member") + XCTAssertEqual(root.elements[1].elements[3].elements[3].elements[0].stringValue, "qux") + } + + func testEscapableCharacters_removeWhitespaceElements() { + let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: true) + let xmlData = + """ + + escaped data: &lt; + + """.data(using: .utf8)! + let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + + XCTAssertEqual(root.key, "SomeType") + XCTAssertEqual(root.elements[0].key, "strValue") + XCTAssertEqual(root.elements[0].elements[0].stringValue, "escaped data: <\r\n") + } + + func testEscapableCharacters() { + let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: false) + let xmlData = + """ + + escaped data: &lt; + + """.data(using: .utf8)! + let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + XCTAssertEqual(root.key, "SomeType") + XCTAssertEqual(root.elements[0].key, "") + XCTAssertEqual(root.elements[0].stringValue, "\n ") + XCTAssertEqual(root.elements[1].elements[0].stringValue, "escaped data: <\r\n") + XCTAssertEqual(root.elements[2].stringValue, "\n") + } } From 9409c968a109e2e5eee067b8b62c47320c4f2208 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:22 -0700 Subject: [PATCH 03/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index e094e612..ccac9411 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -151,7 +151,7 @@ class XMLStackParserTests: XCTestCase { escaped data: &lt; """.data(using: .utf8)! - let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + let root = try parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) XCTAssertEqual(root.key, "SomeType") XCTAssertEqual(root.elements[0].key, "") XCTAssertEqual(root.elements[0].stringValue, "\n ") From b022f184d5753d42fbfe874c019e81d4acd2be17 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:26 -0700 Subject: [PATCH 04/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index ccac9411..68a28f62 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -143,7 +143,7 @@ class XMLStackParserTests: XCTestCase { XCTAssertEqual(root.elements[0].elements[0].stringValue, "escaped data: <\r\n") } - func testEscapableCharacters() { + func testEscapableCharacters() throws { let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: false) let xmlData = """ From f7fc8ade65e7893b7693c19329990b8932b0a575 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:30 -0700 Subject: [PATCH 05/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 68a28f62..fe05228a 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -136,7 +136,7 @@ class XMLStackParserTests: XCTestCase { escaped data: &lt; """.data(using: .utf8)! - let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + let root = try parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) XCTAssertEqual(root.key, "SomeType") XCTAssertEqual(root.elements[0].key, "strValue") From b77b1a476ffec3f5c385e6f1e34d372f213c7032 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:35 -0700 Subject: [PATCH 06/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index fe05228a..8f189ee7 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -128,7 +128,7 @@ class XMLStackParserTests: XCTestCase { XCTAssertEqual(root.elements[1].elements[3].elements[3].elements[0].stringValue, "qux") } - func testEscapableCharacters_removeWhitespaceElements() { + func testEscapableCharacters_removeWhitespaceElements() throws { let parser = XMLStackParser(trimValueWhitespaces: false, removeWhitespaceElements: true) let xmlData = """ From c789604e37428c86c2d8b047086ae2222486ba22 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:38 -0700 Subject: [PATCH 07/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 8f189ee7..19218dcf 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -106,7 +106,7 @@ class XMLStackParserTests: XCTestCase { """.data(using: .utf8)! - let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + let root = try parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) XCTAssertEqual(root.elements[0].key, "") XCTAssertEqual(root.elements[0].stringValue, "\n ") From 431aad00ecf2a77fc1b5e8a6b97a03349b6d50a5 Mon Sep 17 00:00:00 2001 From: John Woo <1911939+wooj2@users.noreply.github.com> Date: Thu, 29 Jul 2021 09:23:43 -0700 Subject: [PATCH 08/12] Update Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift Co-authored-by: Max Desiatov --- Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 19218dcf..94588b1f 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -74,7 +74,7 @@ class XMLStackParserTests: XCTestCase { """.data(using: .utf8)! - let root = try! parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) + let root = try parser.parse(with: xmlData, errorContextLength: 0, shouldProcessNamespaces: false) XCTAssertEqual(root.elements[0].key, "nestedStringList") From c9bd30e8a5233a2d4276c2815fea719753c0a1b8 Mon Sep 17 00:00:00 2001 From: John Woo Date: Thu, 29 Jul 2021 09:26:21 -0700 Subject: [PATCH 09/12] updating per kneekey23 review --- Sources/XMLCoder/Auxiliaries/XMLStackParser.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index c40f427e..ceaceba1 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -157,8 +157,8 @@ extension XMLStackParser: XMLParserDelegate { } func elementWithFilteredElements(element: XMLCoderElement) -> XMLCoderElement { - var hasWhitespaceElements: Bool = false - var hasNonWhitespaceElements: Bool = false + var hasWhitespaceElements = false + var hasNonWhitespaceElements = false var filteredElements: [XMLCoderElement] = [] for ele in element.elements { if ele.isWhitespaceWithNoElements() { @@ -168,13 +168,11 @@ extension XMLStackParser: XMLParserDelegate { filteredElements.append(ele) } } - let updatedElement: XMLCoderElement + if hasWhitespaceElements && hasNonWhitespaceElements { - updatedElement = XMLCoderElement(key: element.key, elements: filteredElements, attributes: element.attributes) - } else { - updatedElement = element + return XMLCoderElement(key: element.key, elements: filteredElements, attributes: element.attributes) } - return updatedElement + return element } func parser(_: XMLParser, foundCharacters string: String) { From 34ce690957420151078a028fa73fbdb3d0edae48 Mon Sep 17 00:00:00 2001 From: John Woo Date: Thu, 29 Jul 2021 11:02:42 -0700 Subject: [PATCH 10/12] adding additional unit tests --- .../Minimal/NestedStringList.swift | 74 +++++++++++++++++++ Tests/XMLCoderTests/Minimal/StringTests.swift | 14 ++++ 2 files changed, 88 insertions(+) create mode 100644 Tests/XMLCoderTests/Minimal/NestedStringList.swift diff --git a/Tests/XMLCoderTests/Minimal/NestedStringList.swift b/Tests/XMLCoderTests/Minimal/NestedStringList.swift new file mode 100644 index 00000000..0dad92de --- /dev/null +++ b/Tests/XMLCoderTests/Minimal/NestedStringList.swift @@ -0,0 +1,74 @@ +// Copyright (c) 2018-2020 XMLCoder contributors +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT +// +// Created by John Woo on 7/29/21. +// + +import Foundation + +import XCTest +@testable import XMLCoder + +class NestedStringList: XCTestCase { + + struct TypeWithNestedStringList: Decodable { + let nestedStringList: [[String]] + + enum CodingKeys: String, CodingKey { + case nestedStringList + } + + enum NestedMemberKeys: String, CodingKey { + case member + } + + public init (from decoder: Decoder) throws { + let containerValues = try decoder.container(keyedBy: CodingKeys.self) + let nestedStringListWrappedContainer = try containerValues.nestedContainer(keyedBy: NestedMemberKeys.self, forKey: .nestedStringList) + let nestedStringListContainer = try nestedStringListWrappedContainer.decodeIfPresent([[String]].self, forKey: .member) + var nestedStringListBuffer:[[String]] = [] + if let nestedStringListContainer = nestedStringListContainer { + nestedStringListBuffer = [[String]]() + var listBuffer0: [String]? = nil + for listContainer0 in nestedStringListContainer { + listBuffer0 = [String]() + for stringContainer1 in listContainer0 { + listBuffer0?.append(stringContainer1) + } + if let listBuffer0 = listBuffer0 { + nestedStringListBuffer.append(listBuffer0) + } + } + } + nestedStringList = nestedStringListBuffer + } + } + + func testRemoveWhitespaceElements() throws { + let decoder = XMLDecoder(trimValueWhitespaces: false, removeWhitespaceElements: true) + let xmlString = + """ + + + + foo: &lt; + bar: &lt; + + + baz: &lt; + qux: &lt; + + + + """ + let xmlData = xmlString.data(using: .utf8)! + + let decoded = try decoder.decode(TypeWithNestedStringList.self, from: xmlData) + XCTAssertEqual(decoded.nestedStringList[0][0], "foo: <\r\n") + XCTAssertEqual(decoded.nestedStringList[0][1], "bar: <\r\n") + XCTAssertEqual(decoded.nestedStringList[1][0], "baz: <\r\n") + XCTAssertEqual(decoded.nestedStringList[1][1], "qux: <\r\n") + } +} diff --git a/Tests/XMLCoderTests/Minimal/StringTests.swift b/Tests/XMLCoderTests/Minimal/StringTests.swift index ea5d6981..aca881f1 100644 --- a/Tests/XMLCoderTests/Minimal/StringTests.swift +++ b/Tests/XMLCoderTests/Minimal/StringTests.swift @@ -79,4 +79,18 @@ class StringTests: XCTestCase { XCTAssertEqual(String(data: encoded, encoding: .utf8)!, xmlString) } } + + func testRemoveWhitespaceElements() throws { + let decoder = XMLDecoder(trimValueWhitespaces: false) + let xmlString = + """ + + escaped data: &lt; + + """ + let xmlData = xmlString.data(using: .utf8)! + + let decoded = try decoder.decode(Container.self, from: xmlData) + XCTAssertEqual(decoded.value, "escaped data: <\r\n") + } } From fdc902196d1daec46347ef607a46834488009037 Mon Sep 17 00:00:00 2001 From: John Woo Date: Thu, 29 Jul 2021 13:53:53 -0700 Subject: [PATCH 11/12] update readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index cf41a19c..3b56448c 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,16 @@ Starting with [version 0.5](https://github.com/MaxDesiatov/XMLCoder/releases/tag you can now set a property `trimValueWhitespaces` to `false` (the default value is `true`) on `XMLDecoder` instance to preserve all whitespaces in decoded strings. + +### Remove whitespace elements + +When decoding pretty-printed XML while `trimValueWhitespaces` is set to `false`, it's possible +for whitespace elements to be added as child elements on an instance of XMLCoderElement. These +whitespace elements make it impossible to decode data structures that require custom `Decodable` logic. +Starting with [version 0.13.0](https://github.com/MaxDesiatov/XMLCoder/releases/tag/0.13.0) you can +set a property `removeWhitespaceElements` to `true` (the default value is `false`) on +`XMLDecoder` to remove these whitespace elements. + ### Choice element coding Starting with [version 0.8](https://github.com/MaxDesiatov/XMLCoder/releases/tag/0.8.0), From 02da5079807d3dab9b2bb56fcd7b2a4f19a18005 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 30 Jul 2021 12:17:45 +0100 Subject: [PATCH 12/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b56448c..4a341946 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ you can now set a property `trimValueWhitespaces` to `false` (the default value ### Remove whitespace elements When decoding pretty-printed XML while `trimValueWhitespaces` is set to `false`, it's possible -for whitespace elements to be added as child elements on an instance of XMLCoderElement. These +for whitespace elements to be added as child elements on an instance of `XMLCoderElement`. These whitespace elements make it impossible to decode data structures that require custom `Decodable` logic. Starting with [version 0.13.0](https://github.com/MaxDesiatov/XMLCoder/releases/tag/0.13.0) you can set a property `removeWhitespaceElements` to `true` (the default value is `false`) on