Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep the order of the attributes during encoding operations #110

Merged
merged 3 commits into from
Jun 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,31 @@

import Foundation

struct Attribute: Equatable {
let key: String
let value: String
}

struct XMLCoderElement: Equatable {
static let attributesKey = "___ATTRIBUTES"
static let escapedCharacterSet = [
private static let attributesKey = "___ATTRIBUTES"
private static let escapedCharacterSet = [
("&", "&"),
("<", "&lt;"),
(">", "&gt;"),
("'", "&apos;"),
("\"", "&quot;"),
]

var key: String
var value: String?
var elements: [XMLCoderElement] = []
var attributes: [String: String] = [:]
let key: String
private(set) var value: String?
private(set) var elements: [XMLCoderElement] = []
private(set) var attributes: [Attribute] = []

init(
key: String,
value: String? = nil,
elements: [XMLCoderElement] = [],
attributes: [String: String] = [:]
attributes: [Attribute] = []
) {
self.key = key
self.value = value
Expand All @@ -47,8 +52,8 @@ struct XMLCoderElement: Equatable {
}

func transformToBoxTree() -> KeyedBox {
let attributes = KeyedStorage(self.attributes.map { key, value in
(key: key, value: StringBox(value) as SimpleBox)
let attributes = KeyedStorage(self.attributes.map { attribute in
(key: attribute.key, value: StringBox(attribute.value) as SimpleBox)
})
let storage = KeyedStorage<String, Box>()
var elements = self.elements.reduce(storage) { $0.merge(element: $1) }
Expand Down Expand Up @@ -125,11 +130,11 @@ struct XMLCoderElement: Equatable {
}

fileprivate func formatXMLAttributes(
from keyValuePairs: [(key: String, value: String)],
from attributes: [Attribute],
into string: inout String
) {
for (key, value) in keyValuePairs {
string += attributeString(key: key, value: value)
for attribute in attributes {
string += attributeString(key: attribute.key, value: attribute.value)
}
}

Expand Down Expand Up @@ -157,9 +162,7 @@ struct XMLCoderElement: Equatable {
}

fileprivate func formatUnsortedXMLAttributes(_ string: inout String) {
formatXMLAttributes(
from: attributes.map { (key: $0, value: $1) }, into: &string
)
formatXMLAttributes(from: attributes, into: &string)
}

private func formatXMLAttributes(
Expand Down Expand Up @@ -278,14 +281,12 @@ extension XMLCoderElement {
}
}

let attributes: [String: String] = Dictionary(
uniqueKeysWithValues: box.attributes.compactMap { key, box in
guard let value = box.xmlString() else {
return nil
}
return (key, value)
let attributes: [Attribute] = box.attributes.compactMap { key, box in
guard let value = box.xmlString() else {
return nil
}
)
return Attribute(key: key, value: value)
}

self.init(key: key, elements: elements, attributes: attributes)
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/XMLCoder/Auxiliaries/XMLStackParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ extension XMLStackParser: XMLParserDelegate {
namespaceURI: String?,
qualifiedName: String?,
attributes attributeDict: [String: String] = [:]) {
let element = XMLCoderElement(key: elementName, attributes: attributeDict)
let attributes = attributeDict.map { key, value in
Attribute(key: key, value: value)
}
let element = XMLCoderElement(key: elementName, attributes: attributes)
stack.append(element)
}

Expand Down
8 changes: 4 additions & 4 deletions Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class XMLElementTests: XCTestCase {
XCTAssertEqual(null.key, "foo")
XCTAssertNil(null.value)
XCTAssertEqual(null.elements, [])
XCTAssertEqual(null.attributes, [:])
XCTAssertEqual(null.attributes, [])
}

func testInitUnkeyed() {
Expand All @@ -24,7 +24,7 @@ class XMLElementTests: XCTestCase {
XCTAssertEqual(keyed.key, "foo")
XCTAssertNil(keyed.value)
XCTAssertEqual(keyed.elements, [])
XCTAssertEqual(keyed.attributes, [:])
XCTAssertEqual(keyed.attributes, [])
}

func testInitKeyed() {
Expand All @@ -36,7 +36,7 @@ class XMLElementTests: XCTestCase {
XCTAssertEqual(keyed.key, "foo")
XCTAssertNil(keyed.value)
XCTAssertEqual(keyed.elements, [])
XCTAssertEqual(keyed.attributes, ["blee": "42"])
XCTAssertEqual(keyed.attributes, [Attribute(key: "blee", value: "42")])
}

func testInitSimple() {
Expand All @@ -45,6 +45,6 @@ class XMLElementTests: XCTestCase {
XCTAssertEqual(keyed.key, "foo")
XCTAssertEqual(keyed.value, "bar")
XCTAssertEqual(keyed.elements, [])
XCTAssertEqual(keyed.attributes, [:])
XCTAssertEqual(keyed.attributes, [])
}
}
18 changes: 15 additions & 3 deletions Tests/XMLCoderTests/DynamicNodeDecodingTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ private let overlappingKeys = """
private let libraryXMLYN = """
<?xml version="1.0" encoding="UTF-8"?>
<library count="2">
<book id="123">
<book id="123" author="Jack" gender="novel">
<id>123</id>
<author>Jack</author>
<gender>novel</gender>
<title>Cat in the Hat</title>
<category main="Y"><value>Kids</value></category>
<category main="N"><value>Wildlife</value></category>
</book>
<book id="456">
<book id="456" author="Susan" gender="fantastic">
<id>456</id>
<author>Susan</author>
<gender>fantastic</gender>
<title>1984</title>
<category main="Y"><value>Classics</value></category>
<category main="N"><value>News</value></category>
Expand All @@ -42,6 +46,8 @@ private let libraryXMLYNStrategy = """
<count>2</count>
<book title="Cat in the Hat">
<id>123</id>
<author>Jack</author>
<gender>novel</gender>
<category>
<main>true</main>
<value>Kids</value>
Expand All @@ -53,6 +59,8 @@ private let libraryXMLYNStrategy = """
</book>
<book title="1984">
<id>456</id>
<author>Susan</author>
<gender>fantastic</gender>
<category>
<main>true</main>
<value>Classics</value>
Expand Down Expand Up @@ -109,18 +117,22 @@ private struct Library: Codable, Equatable, DynamicNodeDecoding {

private struct Book: Codable, Equatable, DynamicNodeEncoding {
let id: UInt
let author: String
let gender: String
let title: String
let categories: [Category]

enum CodingKeys: String, CodingKey {
case id
case author
case gender
case title
case categories = "category"
}

static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
switch key {
case Book.CodingKeys.id: return .both
case Book.CodingKeys.id, Book.CodingKeys.author, Book.CodingKeys.gender: return .both
default: return .element
}
}
Expand Down
30 changes: 25 additions & 5 deletions Tests/XMLCoderTests/DynamicNodeEncodingTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import XCTest
private let libraryXMLYN = """
<?xml version="1.0" encoding="UTF-8"?>
<library count="2">
<book id="123">
<book id="123" author="Jack" gender="novel">
<id>123</id>
<author>Jack</author>
<gender>novel</gender>
<title>Cat in the Hat</title>
<category main="Y"><value>Kids</value></category>
<category main="N"><value>Wildlife</value></category>
</book>
<book id="456">
<book id="456" author="Susan" gender="fantastic">
<id>456</id>
<author>Susan</author>
<gender>fantastic</gender>
<title>1984</title>
<category main="Y"><value>Classics</value></category>
<category main="N"><value>News</value></category>
Expand All @@ -33,6 +37,8 @@ private let libraryXMLYNStrategy = """
<count>2</count>
<book title="Cat in the Hat">
<id>123</id>
<author>Jack</author>
<gender>novel</gender>
<category>
<main>true</main>
<value>Kids</value>
Expand All @@ -44,6 +50,8 @@ private let libraryXMLYNStrategy = """
</book>
<book title="1984">
<id>456</id>
<author>Susan</author>
<gender>fantastic</gender>
<category>
<main>true</main>
<value>Classics</value>
Expand All @@ -60,8 +68,10 @@ private let libraryXMLTrueFalse = """
<?xml version="1.0" encoding="UTF-8"?>
<library>
<count>2</count>
<book id="123">
<book id="123" author="Jack" gender="novel">
<id>123</id>
<author>Jack</author>
<gender>novel</gender>
<title>Cat in the Hat</title>
<category main="true">
<value>Kids</value>
Expand All @@ -70,8 +80,10 @@ private let libraryXMLTrueFalse = """
<value>Wildlife</value>
</category>
</book>
<book id="456">
<book id="456" author="Susan" gender="fantastic">
<id>456</id>
<author>Susan</author>
<gender>fantastic</gender>
<title>1984</title>
<category main="true">
<value>Classics</value>
Expand All @@ -95,18 +107,22 @@ private struct Library: Codable, Equatable {

private struct Book: Codable, Equatable, DynamicNodeEncoding {
let id: UInt
let author: String
let gender: String
let title: String
let categories: [Category]

enum CodingKeys: String, CodingKey {
case id
case author
case gender
case title
case categories = "category"
}

static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
switch key {
case Book.CodingKeys.id: return .both
case Book.CodingKeys.id, Book.CodingKeys.author, Book.CodingKeys.gender: return .both
default: return .element
}
}
Expand Down Expand Up @@ -135,6 +151,8 @@ final class DynamicNodeEncodingTest: XCTestCase {
func testEncode() throws {
let book1 = Book(
id: 123,
author: "Jack",
gender: "novel",
title: "Cat in the Hat",
categories: [
Category(main: true, value: "Kids"),
Expand All @@ -144,6 +162,8 @@ final class DynamicNodeEncodingTest: XCTestCase {

let book2 = Book(
id: 456,
author: "Susan",
gender: "fantastic",
title: "1984",
categories: [
Category(main: true, value: "Classics"),
Expand Down
6 changes: 3 additions & 3 deletions Tests/XMLCoderTests/Minimal/BoxTreeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ class BoxTreeTests: XCTestCase {
key: "foo",
value: "456",
elements: [],
attributes: ["id": "123"]
attributes: [Attribute(key: "id", value: "123")]
)
let e2 = XMLCoderElement(
key: "foo",
value: "123",
elements: [],
attributes: ["id": "789"]
attributes: [Attribute(key: "id", value: "789")]
)
let root = XMLCoderElement(
key: "container",
value: nil,
elements: [e1, e2],
attributes: [:]
attributes: []
)

let boxTree = root.transformToBoxTree()
Expand Down