diff --git a/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift
index 9d572993..ab92c55b 100644
--- a/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift
+++ b/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift
@@ -104,6 +104,13 @@ extension KeyedBox: Box {
}
}
+extension KeyedBox {
+ var value: SimpleBox? {
+ guard elements.count == 1, let value = elements["value"] as? SimpleBox ?? elements[""] as? SimpleBox, !value.isNull else { return nil }
+ return value
+ }
+}
+
extension KeyedBox: CustomStringConvertible {
var description: String {
return "{attributes: \(attributes), elements: \(elements)}"
diff --git a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
index fc1f5cd6..409b3505 100644
--- a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
+++ b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
@@ -47,7 +47,7 @@ struct XMLCoderElement: Equatable {
func flatten() -> KeyedBox {
let attributes = self.attributes.mapValues { StringBox($0) }
- let keyedElements: [String: Box] = elements.reduce([String: Box]()) { (result, element) -> [String: Box] in
+ var keyedElements: [String: Box] = elements.reduce([String: Box]()) { (result, element) -> [String: Box] in
var result = result
let key = element.key
@@ -93,6 +93,11 @@ struct XMLCoderElement: Equatable {
return result
}
+ // Handle attributed unkeyed valye zap
+ // Value should be zap. Detect only when no other elements exist
+ if keyedElements.isEmpty, let value = value {
+ keyedElements["value"] = StringBox(value)
+ }
let keyedBox = KeyedBox(elements: keyedElements, attributes: attributes)
return keyedBox
diff --git a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift
index 37e48534..699c4067 100644
--- a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift
+++ b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift
@@ -308,7 +308,6 @@ extension XMLDecoderImplementation {
if type == Date.self || type == NSDate.self {
let date: Date = try unbox(box)
-
decoded = date as? T
} else if type == Data.self || type == NSData.self {
let data: Data = try unbox(box)
diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift
index b1f4d55d..5e7904b9 100644
--- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift
+++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift
@@ -126,15 +126,18 @@ struct XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol {
public func decode(
_ type: T.Type, forKey key: Key
) throws -> T {
- let attributeNotFound = container.withShared { keyedBox in
- keyedBox.attributes[key.stringValue] == nil
+ let attributeFound = container.withShared { keyedBox in
+ keyedBox.attributes[key.stringValue] != nil
}
- let elementNotFound = container.withShared { keyedBox in
- keyedBox.elements[key.stringValue] == nil
+
+ let elementFound = container.withShared { keyedBox -> Bool in
+ keyedBox.elements[key.stringValue] != nil || keyedBox.value != nil
}
- if let type = type as? AnyEmptySequence.Type, attributeNotFound,
- elementNotFound, let result = type.init() as? T {
+ if let type = type as? AnyEmptySequence.Type,
+ !attributeFound,
+ !elementFound,
+ let result = type.init() as? T {
return result
}
@@ -163,8 +166,12 @@ struct XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol {
_ type: T.Type,
forKey key: Key
) throws -> T {
- let elementOrNil = container.withShared { keyedBox in
- keyedBox.elements[key.stringValue]
+ let elementOrNil = container.withShared { keyedBox -> KeyedBox.Element? in
+ if ["value", ""].contains(key.stringValue) {
+ return keyedBox.value
+ } else {
+ return keyedBox.elements[key.stringValue]
+ }
}
let attributeOrNil = container.withShared { keyedBox in
diff --git a/Tests/XMLCoderTests/AttributedIntrinsicTest.swift b/Tests/XMLCoderTests/AttributedIntrinsicTest.swift
new file mode 100644
index 00000000..51b2990e
--- /dev/null
+++ b/Tests/XMLCoderTests/AttributedIntrinsicTest.swift
@@ -0,0 +1,102 @@
+//
+// AttributedIntrinsicTest.swift
+// XMLCoderTests
+//
+// Created by Joseph Mattiello on 1/23/19.
+//
+
+import Foundation
+import XCTest
+@testable import XMLCoder
+
+let fooXML = """
+
+456
+""".data(using: .utf8)!
+
+private struct Foo: Codable, DynamicNodeEncoding {
+ let id: String
+ let value: String
+
+ enum CodingKeys: String, CodingKey {
+ case id
+ case value
+ }
+
+ static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
+ switch key {
+ case CodingKeys.id:
+ return .attribute
+ default:
+ return .element
+ }
+ }
+}
+
+private struct FooEmptyKeyed: Codable, DynamicNodeEncoding {
+ let id: String
+ let unkeyedValue: Int
+
+ enum CodingKeys: String, CodingKey {
+ case id
+ case unkeyedValue = ""
+ }
+
+ static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
+ switch key {
+ case CodingKeys.id:
+ return .attribute
+ default:
+ return .element
+ }
+ }
+}
+
+final class AttributedIntrinsicTest: XCTestCase {
+
+ func testEncode() {
+ let encoder = XMLEncoder()
+ encoder.outputFormatting = []
+
+ let foo1 = FooEmptyKeyed(id: "123", unkeyedValue: 456)
+
+ let header = XMLHeader(version: 1.0, encoding: "UTF-8")
+ do {
+ let encoded = try encoder.encode(foo1, withRootKey: "foo", header: header)
+ let xmlString = String(data: encoded, encoding: .utf8)
+ XCTAssertNotNil(xmlString)
+ print(xmlString!)
+
+ // Test string equivlancy
+ let encodedXML = xmlString!.trimmingCharacters(in: .whitespacesAndNewlines)
+ let originalXML = String(data: fooXML, encoding: .utf8)!.trimmingCharacters(in: .whitespacesAndNewlines)
+ XCTAssertEqual(encodedXML, originalXML)
+ } catch {
+ print("Test threw error: " + error.localizedDescription)
+ XCTFail(error.localizedDescription)
+ }
+ }
+
+ func testDecode() {
+ do {
+ let decoder = XMLDecoder()
+ decoder.errorContextLength = 10
+
+ let foo1 = try decoder.decode(Foo.self, from: fooXML)
+ XCTAssertEqual(foo1.id, "123")
+ XCTAssertEqual(foo1.value, "456")
+
+ let foo2 = try decoder.decode(FooEmptyKeyed.self, from: fooXML)
+ XCTAssertEqual(foo2.id, "123")
+ XCTAssertEqual(foo2.unkeyedValue, 456)
+ } catch {
+ print("Test threw error: " + error.localizedDescription)
+ XCTFail(error.localizedDescription)
+ }
+ }
+
+ static var allTests = [
+ ("testEncode", testEncode),
+ ("testDecode", testDecode),
+ ]
+}
diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj
index 48057411..d1fa530b 100644
--- a/XMLCoder.xcodeproj/project.pbxproj
+++ b/XMLCoder.xcodeproj/project.pbxproj
@@ -26,6 +26,7 @@
A61FE03C21E4EAB10015D993 /* KeyedIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */; };
B34B3C08220381AC00BCBA30 /* String+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34B3C07220381AB00BCBA30 /* String+ExtensionsTests.swift */; };
B35157CE21F986DD009CA0CC /* DynamicNodeEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35157CD21F986DD009CA0CC /* DynamicNodeEncoding.swift */; };
+ B3B6902E220A71DF0084D407 /* AttributedIntrinsicTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B6902D220A71DF0084D407 /* AttributedIntrinsicTest.swift */; };
B3BE1D612202C1F600259831 /* DynamicNodeEncodingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */; };
B3BE1D632202CB1400259831 /* XMLEncoderImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BE1D622202CB1400259831 /* XMLEncoderImplementation.swift */; };
B3BE1D652202CB7200259831 /* XMLEncoderImplementation+SingleValueEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BE1D642202CB7200259831 /* XMLEncoderImplementation+SingleValueEncodingContainer.swift */; };
@@ -135,6 +136,7 @@
A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedIntTests.swift; sourceTree = ""; };
B34B3C07220381AB00BCBA30 /* String+ExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+ExtensionsTests.swift"; sourceTree = ""; };
B35157CD21F986DD009CA0CC /* DynamicNodeEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicNodeEncoding.swift; sourceTree = ""; };
+ B3B6902D220A71DF0084D407 /* AttributedIntrinsicTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributedIntrinsicTest.swift; sourceTree = ""; };
B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicNodeEncodingTest.swift; sourceTree = ""; };
B3BE1D622202CB1400259831 /* XMLEncoderImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLEncoderImplementation.swift; sourceTree = ""; };
B3BE1D642202CB7200259831 /* XMLEncoderImplementation+SingleValueEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XMLEncoderImplementation+SingleValueEncodingContainer.swift"; sourceTree = ""; };
@@ -378,6 +380,7 @@
OBJ_38 /* RelationshipsTest.swift */,
BF63EF1D21CEC99A001D38C5 /* BenchmarkTests.swift */,
D1FC040421C7EF8200065B43 /* RJISample.swift */,
+ B3B6902D220A71DF0084D407 /* AttributedIntrinsicTest.swift */,
B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */,
A61DCCD621DF8DB300C0A19D /* ClassTests.swift */,
D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */,
@@ -618,6 +621,7 @@
A61FE03921E4D60B0015D993 /* UnkeyedIntTests.swift in Sources */,
BF63EF6B21D10284001D38C5 /* XMLElementTests.swift in Sources */,
BF9457ED21CBB6BC005ACFDE /* BoolTests.swift in Sources */,
+ B3B6902E220A71DF0084D407 /* AttributedIntrinsicTest.swift in Sources */,
D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */,
BF63EF0A21CD7C1A001D38C5 /* URLTests.swift in Sources */,
BF9457CE21CBB516005ACFDE /* StringBoxTests.swift in Sources */,