diff --git a/.swiftlint.yml b/.swiftlint.yml index 7b6b0463..501f19e1 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,6 +5,7 @@ disabled_rules: - operator_whitespace - function_parameter_count - opening_brace +- nesting line_length: - 150 diff --git a/Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift b/Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift index 123a3afe..c359d529 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift @@ -21,10 +21,6 @@ struct BoolBox: Equatable { case _: return nil } } - - func unbox() -> Unboxed { - return unboxed - } } extension BoolBox: Box { diff --git a/Sources/XMLCoder/Auxiliaries/Box/DataBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DataBox.swift index 0af1e535..6896121b 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/DataBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/DataBox.swift @@ -29,10 +29,6 @@ struct DataBox: Equatable { self.init(data, format: .base64) } - func unbox() -> Unboxed { - return unboxed - } - func xmlString(format: Format) -> String { switch format { case .base64: diff --git a/Sources/XMLCoder/Auxiliaries/Box/DateBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DateBox.swift index c22df2f1..1f49c441 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/DateBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/DateBox.swift @@ -59,10 +59,6 @@ struct DateBox: Equatable { self.init(date, format: .formatter(formatter)) } - func unbox() -> Unboxed { - return unboxed - } - func xmlString(format: Format) -> String { switch format { case .secondsSince1970: diff --git a/Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift index 33781fe8..ea9d85c9 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift @@ -22,10 +22,6 @@ struct DecimalBox: Equatable { } self.init(unboxed) } - - func unbox() -> Unboxed { - return unboxed - } } extension DecimalBox: Box { diff --git a/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift index d9ce4c18..dce3a1e9 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift @@ -16,7 +16,7 @@ struct KeyedBox { var elements = Elements() var attributes = Attributes() - func unbox() -> (elements: Elements, attributes: Attributes) { + var unboxed: (elements: Elements, attributes: Attributes) { return ( elements: elements, attributes: attributes diff --git a/Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift index fe18fc4a..a1aa9d70 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift @@ -6,7 +6,7 @@ // class SharedBox { - fileprivate var unboxed: Unboxed + private(set) var unboxed: Unboxed init(_ wrapped: Unboxed) { unboxed = wrapped diff --git a/Sources/XMLCoder/Auxiliaries/Box/StringBox.swift b/Sources/XMLCoder/Auxiliaries/Box/StringBox.swift index 9842dab1..dcc14f6d 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/StringBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/StringBox.swift @@ -17,10 +17,6 @@ struct StringBox: Equatable { init(xmlString: Unboxed) { self.init(xmlString) } - - func unbox() -> Unboxed { - return unboxed - } } extension StringBox: Box { diff --git a/Sources/XMLCoder/Auxiliaries/Box/URLBox.swift b/Sources/XMLCoder/Auxiliaries/Box/URLBox.swift index 86ecbe3c..d8e2c3de 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/URLBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/URLBox.swift @@ -22,10 +22,6 @@ struct URLBox: Equatable { } self.init(unboxed) } - - func unbox() -> Unboxed { - return unboxed - } } extension URLBox: Box { diff --git a/Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift index 06919421..ba58c67a 100644 --- a/Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift +++ b/Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift @@ -29,10 +29,6 @@ struct UnkeyedBox { self.unboxed = unboxed } - func unbox() -> Unboxed { - return unboxed - } - mutating func append(_ newElement: Element) { unboxed.append(newElement) } diff --git a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift index 508c2161..39519236 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift @@ -35,9 +35,11 @@ struct XMLCoderElement: Equatable { } mutating func append(value string: String) { - var value = self.value ?? "" - value += string.trimmingCharacters(in: .whitespacesAndNewlines) - self.value = value + guard value != nil else { + value = string + return + } + value?.append(string) } mutating func append(element: XMLCoderElement, forKey key: String) { @@ -255,7 +257,7 @@ extension XMLCoderElement { switch box { case let sharedUnkeyedBox as SharedBox: - let box = sharedUnkeyedBox.unbox() + let box = sharedUnkeyedBox.unboxed elements.append(contentsOf: box.map { XMLCoderElement(key: key, box: $0) }) @@ -265,7 +267,7 @@ extension XMLCoderElement { XMLCoderElement(key: key, box: $0) }) case let sharedKeyedBox as SharedBox: - let box = sharedKeyedBox.unbox() + let box = sharedKeyedBox.unboxed elements.append(XMLCoderElement(key: key, box: box)) case let keyedBox as KeyedBox: elements.append(XMLCoderElement(key: key, box: keyedBox)) @@ -296,9 +298,9 @@ extension XMLCoderElement { init(key: String, box: Box) { switch box { case let sharedUnkeyedBox as SharedBox: - self.init(key: key, box: sharedUnkeyedBox.unbox()) + self.init(key: key, box: sharedUnkeyedBox.unboxed) case let sharedKeyedBox as SharedBox: - self.init(key: key, box: sharedKeyedBox.unbox()) + self.init(key: key, box: sharedKeyedBox.unboxed) case let unkeyedBox as UnkeyedBox: self.init(key: key, box: unkeyedBox) case let keyedBox as KeyedBox: diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index 6d9b31a3..8bc40552 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -11,13 +11,20 @@ import Foundation class XMLStackParser: NSObject { var root: XMLCoderElement? private var stack: [XMLCoderElement] = [] + private let trimValueWhitespaces: Bool + + init(trimValueWhitespaces: Bool = true) { + self.trimValueWhitespaces = trimValueWhitespaces + super.init() + } static func parse( with data: Data, errorContextLength length: UInt, - shouldProcessNamespaces: Bool + shouldProcessNamespaces: Bool, + trimValueWhitespaces: Bool ) throws -> KeyedBox { - let parser = XMLStackParser() + let parser = XMLStackParser(trimValueWhitespaces: trimValueWhitespaces) let node = try parser.parse( with: data, @@ -99,6 +106,13 @@ class XMLStackParser: NSObject { } try body(&stack[stack.count - 1]) } + + /// Trim whitespaces for a given string if needed. + func process(string: String) -> String { + return trimValueWhitespaces + ? string.trimmingCharacters(in: .whitespacesAndNewlines) + : string + } } extension XMLStackParser: XMLParserDelegate { @@ -120,14 +134,10 @@ extension XMLStackParser: XMLParserDelegate { didEndElement _: String, namespaceURI _: String?, qualifiedName _: String?) { - guard var element = stack.popLast() else { + guard let element = stack.popLast() else { return } - if let value = element.value { - element.value = value.isEmpty ? nil : value - } - withCurrentElement { currentElement in currentElement.append(element: element, forKey: element.key) } @@ -139,7 +149,7 @@ extension XMLStackParser: XMLParserDelegate { func parser(_: XMLParser, foundCharacters string: String) { withCurrentElement { currentElement in - currentElement.append(value: string) + currentElement.append(value: process(string: string)) } } @@ -147,8 +157,9 @@ extension XMLStackParser: XMLParserDelegate { guard let string = String(data: CDATABlock, encoding: .utf8) else { return } + withCurrentElement { currentElement in - currentElement.append(value: string) + currentElement.append(value: process(string: string)) } } } diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index 7da6f2ca..9985912a 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -284,6 +284,11 @@ open class XMLDecoder { */ open var shouldProcessNamespaces: Bool = false + /** A boolean value that determines whether the parser trims whitespaces + and newlines from the end and the beginning of string values. + */ + open var trimValueWhitespaces: Bool + /// Options set on the top-level encoder to pass down the decoding hierarchy. struct Options { let dateDecodingStrategy: DateDecodingStrategy @@ -309,7 +314,9 @@ open class XMLDecoder { // MARK: - Constructing a XML Decoder /// Initializes `self` with default strategies. - public init() {} + public init(trimValueWhitespaces: Bool = true) { + self.trimValueWhitespaces = trimValueWhitespaces + } // MARK: - Decoding Values @@ -327,7 +334,8 @@ open class XMLDecoder { let topLevel: Box = try XMLStackParser.parse( with: data, errorContextLength: errorContextLength, - shouldProcessNamespaces: shouldProcessNamespaces + shouldProcessNamespaces: shouldProcessNamespaces, + trimValueWhitespaces: trimValueWhitespaces ) let decoder = XMLDecoderImplementation( @@ -346,13 +354,6 @@ open class XMLDecoder { _ = decoder.nodeDecodings.removeLast() } - guard let box: T = try decoder.unbox(topLevel) else { - throw DecodingError.valueNotFound(type, DecodingError.Context( - codingPath: [], - debugDescription: "The given data did not contain a top-level box." - )) - } - - return box + return try decoder.unbox(topLevel) } } diff --git a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift index 4dd5c76b..d6212eb3 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift @@ -157,6 +157,13 @@ extension XMLDecoderImplementation { codingPath: codingPath, debugDescription: "Expected \(valueType) but found null instead." )) + case let keyedBox as KeyedBox: + guard + let value = keyedBox.elements["value"] as? B + else { + fallthrough + } + return value default: throw DecodingError._typeMismatch( at: codingPath, @@ -168,29 +175,29 @@ extension XMLDecoderImplementation { func unbox(_ box: Box) throws -> Bool { let stringBox: StringBox = try typedBox(box, for: Bool.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let boolBox = BoolBox(xmlString: string) else { throw DecodingError._typeMismatch(at: codingPath, expectation: Bool.self, reality: box) } - return boolBox.unbox() + return boolBox.unboxed } func unbox(_ box: Box) throws -> Decimal { let stringBox: StringBox = try typedBox(box, for: Decimal.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let decimalBox = DecimalBox(xmlString: string) else { throw DecodingError._typeMismatch(at: codingPath, expectation: Decimal.self, reality: box) } - return decimalBox.unbox() + return decimalBox.unboxed } func unbox(_ box: Box) throws -> T { let stringBox: StringBox = try typedBox(box, for: T.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let intBox = IntBox(xmlString: string) else { throw DecodingError._typeMismatch(at: codingPath, expectation: T.self, reality: box) @@ -208,7 +215,7 @@ extension XMLDecoderImplementation { func unbox(_ box: Box) throws -> T { let stringBox: StringBox = try typedBox(box, for: T.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let uintBox = UIntBox(xmlString: string) else { throw DecodingError._typeMismatch(at: codingPath, expectation: T.self, reality: box) @@ -226,7 +233,7 @@ extension XMLDecoderImplementation { func unbox(_ box: Box) throws -> T { let stringBox: StringBox = try typedBox(box, for: T.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let floatBox = FloatBox(xmlString: string) else { throw DecodingError._typeMismatch(at: codingPath, expectation: T.self, reality: box) @@ -244,7 +251,7 @@ extension XMLDecoderImplementation { func unbox(_ box: Box) throws -> String { let stringBox: StringBox = try typedBox(box, for: String.self) - let string = stringBox.unbox() + let string = stringBox.unboxed return string } @@ -258,7 +265,7 @@ extension XMLDecoderImplementation { case .secondsSince1970: let stringBox: StringBox = try typedBox(box, for: Date.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let dateBox = DateBox(secondsSince1970: string) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -266,10 +273,10 @@ extension XMLDecoderImplementation { debugDescription: "Expected date string to be formatted in seconds since 1970." )) } - return dateBox.unbox() + return dateBox.unboxed case .millisecondsSince1970: let stringBox: StringBox = try typedBox(box, for: Date.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let dateBox = DateBox(millisecondsSince1970: string) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -277,10 +284,10 @@ extension XMLDecoderImplementation { debugDescription: "Expected date string to be formatted in milliseconds since 1970." )) } - return dateBox.unbox() + return dateBox.unboxed case .iso8601: let stringBox: StringBox = try typedBox(box, for: Date.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let dateBox = DateBox(iso8601: string) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -288,10 +295,10 @@ extension XMLDecoderImplementation { debugDescription: "Expected date string to be ISO8601-formatted." )) } - return dateBox.unbox() + return dateBox.unboxed case let .formatted(formatter): let stringBox: StringBox = try typedBox(box, for: Date.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let dateBox = DateBox(xmlString: string, formatter: formatter) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -299,7 +306,7 @@ extension XMLDecoderImplementation { debugDescription: "Date string does not match format expected by formatter." )) } - return dateBox.unbox() + return dateBox.unboxed case let .custom(closure): storage.push(container: box) defer { storage.popContainer() } @@ -315,7 +322,7 @@ extension XMLDecoderImplementation { return try Data(from: self) case .base64: let stringBox: StringBox = try typedBox(box, for: Data.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let dataBox = DataBox(base64: string) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -323,7 +330,7 @@ extension XMLDecoderImplementation { debugDescription: "Encountered Data is not valid Base64" )) } - return dataBox.unbox() + return dataBox.unboxed case let .custom(closure): storage.push(container: box) defer { storage.popContainer() } @@ -333,7 +340,7 @@ extension XMLDecoderImplementation { func unbox(_ box: Box) throws -> URL { let stringBox: StringBox = try typedBox(box, for: URL.self) - let string = stringBox.unbox() + let string = stringBox.unboxed guard let urlBox = URLBox(xmlString: string) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -342,7 +349,7 @@ extension XMLDecoderImplementation { )) } - return urlBox.unbox() + return urlBox.unboxed } private struct TypeMismatch: Error {} @@ -369,8 +376,19 @@ extension XMLDecoderImplementation { decoded = value } else { storage.push(container: box) - decoded = try type.init(from: self) - storage.popContainer() + defer { storage.popContainer() } + + do { + decoded = try type.init(from: self) + } catch { + guard case DecodingError.valueNotFound = error, + let type = type as? AnyOptional.Type, + let result = type.init() as? T else { + throw error + } + + return result + } } guard let result = decoded else { diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index b02d4791..5a558f6f 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -29,6 +29,7 @@ class XMLStackParserTests: XCTestCase { let expected = XMLCoderElement( key: "container", + value: "", elements: [ XMLCoderElement( key: "value", diff --git a/Tests/XMLCoderTests/Box/BoolBoxTests.swift b/Tests/XMLCoderTests/Box/BoolBoxTests.swift index 46a50496..fa628249 100644 --- a/Tests/XMLCoderTests/Box/BoolBoxTests.swift +++ b/Tests/XMLCoderTests/Box/BoolBoxTests.swift @@ -24,7 +24,7 @@ class BoolBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/DataBoxTests.swift b/Tests/XMLCoderTests/Box/DataBoxTests.swift index 48727da3..6246b69b 100644 --- a/Tests/XMLCoderTests/Box/DataBoxTests.swift +++ b/Tests/XMLCoderTests/Box/DataBoxTests.swift @@ -24,7 +24,7 @@ class DataBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed, format: .base64) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/DateBoxTests.swift b/Tests/XMLCoderTests/Box/DateBoxTests.swift index 6448b066..2230d0bb 100644 --- a/Tests/XMLCoderTests/Box/DateBoxTests.swift +++ b/Tests/XMLCoderTests/Box/DateBoxTests.swift @@ -31,7 +31,7 @@ class DateBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed, format: .iso8601) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/DecimalBoxTests.swift b/Tests/XMLCoderTests/Box/DecimalBoxTests.swift index 79db864a..454c1f89 100644 --- a/Tests/XMLCoderTests/Box/DecimalBoxTests.swift +++ b/Tests/XMLCoderTests/Box/DecimalBoxTests.swift @@ -26,7 +26,7 @@ class DecimalBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/KeyedBoxTests.swift b/Tests/XMLCoderTests/Box/KeyedBoxTests.swift index 64de299d..f97470b0 100644 --- a/Tests/XMLCoderTests/Box/KeyedBoxTests.swift +++ b/Tests/XMLCoderTests/Box/KeyedBoxTests.swift @@ -22,7 +22,7 @@ class KeyedBoxTests: XCTestCase { } func testUnbox() { - let (elements, attributes) = box.unbox() + let (elements, attributes) = box.unboxed XCTAssertEqual(elements.count, 2) XCTAssertEqual(elements["foo"] as? StringBox, StringBox("bar")) diff --git a/Tests/XMLCoderTests/Box/SharedBoxTests.swift b/Tests/XMLCoderTests/Box/SharedBoxTests.swift index c80c8248..748e0a2c 100644 --- a/Tests/XMLCoderTests/Box/SharedBoxTests.swift +++ b/Tests/XMLCoderTests/Box/SharedBoxTests.swift @@ -12,7 +12,7 @@ class SharedBoxTests: XCTestCase { func testInit() { let box = SharedBox(BoolBox(false)) box.withShared { shared in - XCTAssertFalse(shared.unbox()) + XCTAssertFalse(shared.unboxed) } } diff --git a/Tests/XMLCoderTests/Box/StringBoxTests.swift b/Tests/XMLCoderTests/Box/StringBoxTests.swift index 9fb2921c..9a2a4186 100644 --- a/Tests/XMLCoderTests/Box/StringBoxTests.swift +++ b/Tests/XMLCoderTests/Box/StringBoxTests.swift @@ -27,7 +27,7 @@ class StringBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/URLBoxTests.swift b/Tests/XMLCoderTests/Box/URLBoxTests.swift index 486267b7..8d9109cf 100644 --- a/Tests/XMLCoderTests/Box/URLBoxTests.swift +++ b/Tests/XMLCoderTests/Box/URLBoxTests.swift @@ -24,7 +24,7 @@ class URLBoxTests: XCTestCase { for unboxed in values { let box = Boxed(unboxed) - XCTAssertEqual(box.unbox(), unboxed) + XCTAssertEqual(box.unboxed, unboxed) } } diff --git a/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift b/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift index 16e50217..a47196be 100644 --- a/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift +++ b/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift @@ -19,7 +19,7 @@ class UnkeyedBoxTests: XCTestCase { } func testUnbox() { - let unboxed = box.unbox() + let unboxed = box.unboxed XCTAssertEqual(unboxed.count, 2) XCTAssertEqual(unboxed[0] as? StringBox, StringBox("foo")) XCTAssertEqual(unboxed[1] as? IntBox, IntBox(42)) diff --git a/Tests/XMLCoderTests/SpacePreserveTest.swift b/Tests/XMLCoderTests/SpacePreserveTest.swift new file mode 100644 index 00000000..89e59464 --- /dev/null +++ b/Tests/XMLCoderTests/SpacePreserveTest.swift @@ -0,0 +1,54 @@ +// +// SpacePreserveTest.swift +// XMLCoder +// +// Created by Max Desiatov on 29/04/2019. +// + +import XCTest +@testable import XMLCoder + +private let simpleXML = """ + +""".data(using: .utf8)! + +private let nestedXML = """ + +""".data(using: .utf8)! + +private struct Item: Codable, Equatable { + public let text: String? + + enum CodingKeys: String, CodingKey { + case text = "t" + } +} + +final class SpacePreserveTest: XCTestCase { + func testSimple() throws { + let result = try XMLDecoder().decode(String.self, from: simpleXML) + XCTAssertTrue(result.isEmpty) + } + + func testSimpleOptional() throws { + let result = try XMLDecoder().decode(String?.self, from: simpleXML) + XCTAssertTrue(result?.isEmpty ?? false) + } + + func testNestedOptional() throws { + let result = try XMLDecoder().decode(Item.self, from: nestedXML) + XCTAssertTrue(result.text?.isEmpty ?? false) + } + + func testUntrimmed() throws { + let result = try XMLDecoder( + trimValueWhitespaces: false + ).decode(String.self, from: simpleXML) + XCTAssertFalse(result.isEmpty) + + let item = try XMLDecoder( + trimValueWhitespaces: false + ).decode(Item.self, from: nestedXML) + XCTAssertFalse(item.text?.isEmpty ?? true) + } +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 3b7079c6..3ec570f7 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -84,6 +84,7 @@ BF9457F521CBB6BC005ACFDE /* DecimalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EA21CBB6BC005ACFDE /* DecimalTests.swift */; }; BF9457F621CBB6BC005ACFDE /* KeyedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EB21CBB6BC005ACFDE /* KeyedTests.swift */; }; BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EC21CBB6BC005ACFDE /* DataTests.swift */; }; + D11815C1227788C8008836E4 /* SpacePreserveTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11815BF227788BA008836E4 /* SpacePreserveTest.swift */; }; D14D8A8621F1D6B300B0D31A /* SingleChildTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */; }; D158F12F2229892C0032B449 /* DynamicNodeDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D158F12E2229892C0032B449 /* DynamicNodeDecoding.swift */; }; D162674321F9B2AF0056D1D8 /* OptionalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D162674121F9B2850056D1D8 /* OptionalTests.swift */; }; @@ -201,6 +202,7 @@ BF9457EA21CBB6BC005ACFDE /* DecimalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecimalTests.swift; sourceTree = ""; }; BF9457EB21CBB6BC005ACFDE /* KeyedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedTests.swift; sourceTree = ""; }; BF9457EC21CBB6BC005ACFDE /* DataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataTests.swift; sourceTree = ""; }; + D11815BF227788BA008836E4 /* SpacePreserveTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacePreserveTest.swift; sourceTree = ""; }; D11979B521F5CD2500A9C574 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; D11979B621F5CD2500A9C574 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D11979B721F5EA5400A9C574 /* XMLCoder.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = XMLCoder.podspec; sourceTree = ""; }; @@ -405,6 +407,7 @@ D1FC040421C7EF8200065B43 /* RJISample.swift */, OBJ_37 /* RJITest.swift */, D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */, + D11815BF227788BA008836E4 /* SpacePreserveTest.swift */, ); name = XMLCoderTests; path = Tests/XMLCoderTests; @@ -647,6 +650,7 @@ BF63EF6B21D10284001D38C5 /* XMLElementTests.swift in Sources */, BF9457ED21CBB6BC005ACFDE /* BoolTests.swift in Sources */, B3B6902E220A71DF0084D407 /* AttributedIntrinsicTest.swift in Sources */, + D11815C1227788C8008836E4 /* SpacePreserveTest.swift in Sources */, D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */, D1EC3E62225A32F500C610E3 /* FlattenTests.swift in Sources */, BF63EF0A21CD7C1A001D38C5 /* URLTests.swift in Sources */,