diff --git a/Sources/XMLCoder/Auxiliaries/XMLElement.swift b/Sources/XMLCoder/Auxiliaries/XMLElement.swift index 734eb1f0..c7f1e5df 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLElement.swift @@ -13,58 +13,57 @@ struct _XMLElement { var key: String var value: String? + var elements: [_XMLElement] = [] var attributes: [String: String] = [:] - var elements: [String: [_XMLElement]] = [:] - init(key: String, value: String? = nil, attributes: [String: String] = [:], elements: [String: [_XMLElement]] = [:]) { + init(key: String, value: String? = nil, elements: [_XMLElement] = [], attributes: [String: String] = [:]) { self.key = key self.value = value - self.attributes = attributes self.elements = elements + self.attributes = attributes } init(key: String, box: UnkeyedBox) { - self.init(key: key) - - elements[key] = box.map { box in + let elements = box.map { box in _XMLElement(key: key, box: box) } + + self.init(key: key, elements: elements) } init(key: String, box: KeyedBox) { - self.init(key: key) - - attributes = Dictionary(uniqueKeysWithValues: box.attributes.compactMap { key, box in - guard let value = box.xmlString() else { - return nil - } - return (key, value) - }) + var elements: [_XMLElement] = [] - elements = Dictionary(uniqueKeysWithValues: box.elements.map { key, box in + for (key, box) in box.elements { switch box { case let sharedUnkeyedBox as SharedBox: let box = sharedUnkeyedBox.unbox() as! UnkeyedBox - let elements = box.map { _XMLElement(key: key, box: $0) } - return (key, elements) + elements.append(contentsOf: box.map { _XMLElement(key: key, box: $0) }) case let unkeyedBox as UnkeyedBox: // This basically injects the unkeyed children directly into self: - let elements = unkeyedBox.map { _XMLElement(key: key, box: $0) } - return (key, elements) + elements.append(contentsOf: unkeyedBox.map { + _XMLElement(key: key, box: $0) + }) case let sharedKeyedBox as SharedBox: let box = sharedKeyedBox.unbox() as! KeyedBox - let elements = [_XMLElement(key: key, box: box)] - return (key, elements) + elements.append(_XMLElement(key: key, box: box)) case let keyedBox as KeyedBox: - let elements = [_XMLElement(key: key, box: keyedBox)] - return (key, elements) + elements.append(_XMLElement(key: key, box: keyedBox)) case let simpleBox as SimpleBox: - let elements = [_XMLElement(key: key, box: simpleBox)] - return (key, elements) + elements.append(_XMLElement(key: key, box: simpleBox)) case let box: preconditionFailure("Unclassified box: \(type(of: box))") } + } + + let attributes: [String: String] = Dictionary(uniqueKeysWithValues: box.attributes.compactMap { key, box in + guard let value = box.xmlString() else { + return nil + } + return (key, value) }) + + self.init(key: key, elements: elements, attributes: attributes) } init(key: String, box: SimpleBox) { @@ -96,54 +95,55 @@ struct _XMLElement { } mutating func append(element: _XMLElement, forKey key: String) { - elements[key, default: []].append(element) + elements.append(element) } func flatten() -> KeyedBox { let attributes = self.attributes.mapValues { StringBox($0) } var elements: [String: Box] = [:] - for (key, value) in self.elements { - for child in value { - let hasValue = child.value != nil - let hasElements = !child.elements.isEmpty - let hasAttributes = !child.attributes.isEmpty - - if hasValue || hasElements || hasAttributes { - if let content = child.value { - switch elements[key] { - case var unkeyedBox as UnkeyedBox: - unkeyedBox.append(StringBox(content)) - elements[key] = unkeyedBox - case let stringBox as StringBox: - elements[key] = UnkeyedBox([stringBox, StringBox(content)]) - default: - elements[key] = StringBox(content) - } - } - if hasElements || hasAttributes { - let content = child.flatten() - switch elements[key] { - case var unkeyedBox as UnkeyedBox: - unkeyedBox.append(content) - elements[key] = unkeyedBox - case let box?: - elements[key] = UnkeyedBox([box, content]) - default: - elements[key] = content - } + + for element in self.elements { + let key = element.key + + let hasValue = element.value != nil + let hasElements = !element.elements.isEmpty + let hasAttributes = !element.attributes.isEmpty + + if hasValue || hasElements || hasAttributes { + if let content = element.value { + switch elements[key] { + case var unkeyedBox as UnkeyedBox: + unkeyedBox.append(StringBox(content)) + elements[key] = unkeyedBox + case let stringBox as StringBox: + elements[key] = UnkeyedBox([stringBox, StringBox(content)]) + default: + elements[key] = StringBox(content) } - } else { + } + if hasElements || hasAttributes { + let content = element.flatten() switch elements[key] { case var unkeyedBox as UnkeyedBox: - unkeyedBox.append(NullBox()) + unkeyedBox.append(content) elements[key] = unkeyedBox case let box?: - elements[key] = UnkeyedBox([box, NullBox()]) + elements[key] = UnkeyedBox([box, content]) default: - elements[key] = NullBox() + elements[key] = content } } + } else { + switch elements[key] { + case var unkeyedBox as UnkeyedBox: + unkeyedBox.append(NullBox()) + elements[key] = unkeyedBox + case let box?: + elements[key] = UnkeyedBox([box, NullBox()]) + default: + elements[key] = NullBox() + } } } @@ -160,15 +160,13 @@ struct _XMLElement { } fileprivate func formatUnsortedXMLElements(_ string: inout String, _ level: Int, _ cdata: Bool, _ formatting: XMLEncoder.OutputFormatting, _ prettyPrinted: Bool) { - formatXMLElements(from: elements.map { (key: $0, value: $1) }, into: &string, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) + formatXMLElements(from: elements, into: &string, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) } - fileprivate func elementString(for element: (key: String, value: [_XMLElement]), at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) -> String { + fileprivate func elementString(for element: _XMLElement, at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) -> String { var string = "" - for child in element.value { - string += child._toXMLString(indented: level + 1, withCDATA: cdata, formatting: formatting) - string += prettyPrinted ? "\n" : "" - } + string += element._toXMLString(indented: level + 1, withCDATA: cdata, formatting: formatting) + string += prettyPrinted ? "\n" : "" return string } @@ -186,7 +184,7 @@ struct _XMLElement { } } - fileprivate func formatXMLElements(from elements: [(key: String, value: [_XMLElement])], into string: inout String, at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) { + fileprivate func formatXMLElements(from elements: [_XMLElement], into string: inout String, at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) { for element in elements { string += elementString(for: element, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift index e4972ec0..fff99f9b 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift @@ -14,7 +14,7 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(null.key, "foo") XCTAssertNil(null.value) - XCTAssertEqual(null.elements, [:]) + XCTAssertEqual(null.elements, []) XCTAssertEqual(null.attributes, [:]) } @@ -23,7 +23,7 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(keyed.key, "foo") XCTAssertNil(keyed.value) - XCTAssertEqual(keyed.elements, ["foo": []]) + XCTAssertEqual(keyed.elements, []) XCTAssertEqual(keyed.attributes, [:]) } @@ -35,7 +35,7 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(keyed.key, "foo") XCTAssertNil(keyed.value) - XCTAssertEqual(keyed.elements, [:]) + XCTAssertEqual(keyed.elements, []) XCTAssertEqual(keyed.attributes, ["blee": "42"]) } @@ -44,7 +44,7 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(keyed.key, "foo") XCTAssertEqual(keyed.value, "bar") - XCTAssertEqual(keyed.elements, [:]) + XCTAssertEqual(keyed.elements, []) XCTAssertEqual(keyed.attributes, [:]) } } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 82b697d0..1b8a2879 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -27,18 +27,14 @@ class XMLStackParserTests: XCTestCase { let expected = _XMLElement( key: "container", elements: [ - "value": [ - _XMLElement( - key: "value", - value: "42" - ), - ], - "data": [ - _XMLElement( - key: "data", - value: "lorem ipsum" - ), - ], + _XMLElement( + key: "value", + value: "42" + ), + _XMLElement( + key: "data", + value: "lorem ipsum" + ), ] ) XCTAssertEqual(root, expected) diff --git a/Tests/XMLCoderTests/RelationshipsTest.swift b/Tests/XMLCoderTests/RelationshipsTest.swift index 550da4fd..febcf0e9 100644 --- a/Tests/XMLCoderTests/RelationshipsTest.swift +++ b/Tests/XMLCoderTests/RelationshipsTest.swift @@ -49,11 +49,11 @@ final class RelationshipsTest: XCTestCase { let decoder = XMLDecoder() decoder.keyDecodingStrategy = .convertFromCapitalized - let rels = try decoder.decode(Relationships.self, from: xml) + let relationships = try decoder.decode(Relationships.self, from: xml) - XCTAssertEqual(rels.items.count, 3) + XCTAssertEqual(relationships.items.count, 3) - guard let relationship = rels.items.first else { + guard let relationship = relationships.items.first else { return }