From 8c2740935a95347a19ace02f8186495340301818 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 30 Dec 2018 16:43:30 +0000 Subject: [PATCH 1/3] Fix decoding of arrays with optional elements Also move protocols for tricks with metatypes to new Metatypes file. --- Sources/XMLCoder/Auxiliaries/Metatypes.swift | 35 ++++++++ Sources/XMLCoder/Auxiliaries/XMLElement.swift | 17 +++- .../XMLCoder/Auxiliaries/XMLStackParser.swift | 17 +++- Sources/XMLCoder/Decoder/XMLDecoder.swift | 6 +- .../Decoder/XMLKeyedDecodingContainer.swift | 90 +++++++++---------- .../Decoder/XMLUnkeyedDecodingContainer.swift | 58 ++++++++---- .../Auxiliary/XMLElementTests.swift | 1 - .../XMLCoderTests/Minimal/UnkeyedTests.swift | 61 +++++++++++++ XMLCoder.xcodeproj/project.pbxproj | 4 + 9 files changed, 212 insertions(+), 77 deletions(-) create mode 100644 Sources/XMLCoder/Auxiliaries/Metatypes.swift diff --git a/Sources/XMLCoder/Auxiliaries/Metatypes.swift b/Sources/XMLCoder/Auxiliaries/Metatypes.swift new file mode 100644 index 00000000..ea0263d9 --- /dev/null +++ b/Sources/XMLCoder/Auxiliaries/Metatypes.swift @@ -0,0 +1,35 @@ +// +// TypeErasure.swift +// XMLCoder +// +// Created by Max Desiatov on 30/12/2018. +// + +/// Type-erased protocol helper for a metatype check in generic `decode` +/// overload. +protocol AnyEmptySequence { + init() +} + +protocol AnyArray { + static var elementType: Any.Type { get } +} + +extension Array: AnyEmptySequence, AnyArray { + static var elementType: Any.Type { + return Element.self + } +} +extension Dictionary: AnyEmptySequence {} + +/// Type-erased protocol helper for a metatype check in generic `decode` +/// overload. +protocol AnyOptional { + init() +} + +extension Optional: AnyOptional { + init() { + self = nil + } +} diff --git a/Sources/XMLCoder/Auxiliaries/XMLElement.swift b/Sources/XMLCoder/Auxiliaries/XMLElement.swift index 86de9391..fec84ec2 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLElement.swift @@ -96,12 +96,11 @@ struct _XMLElement { if let content = child.value { switch elements[key] { case let unkeyedBox as UnkeyedBox: - var boxes = unkeyedBox.unbox() - boxes.append(StringBox(content)) - elements[key] = UnkeyedBox(boxes) + unkeyedBox.append(StringBox(content)) + elements[key] = unkeyedBox case let keyedBox as StringBox: elements[key] = UnkeyedBox([keyedBox, StringBox(content)]) - case _: + default: elements[key] = StringBox(content) } } else if !child.elements.isEmpty || !child.attributes.isEmpty { @@ -117,6 +116,16 @@ struct _XMLElement { } else { elements[key] = content } + } else { + switch elements[key] { + case let unkeyedBox as UnkeyedBox: + unkeyedBox.append(NullBox()) + elements[key] = unkeyedBox + case let keyedBox as StringBox: + elements[key] = UnkeyedBox([keyedBox, NullBox()]) + default: + elements[key] = NullBox() + } } } } diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index 30e9fe51..4ccd7ba2 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -14,7 +14,8 @@ class _XMLStackParser: NSObject { var root: _XMLElement? private var stack: [_XMLElement] = [] - static func parse(with data: Data, errorContextLength length: UInt) throws -> KeyedBox { + static func parse(with data: Data, + errorContextLength length: UInt) throws -> KeyedBox { let parser = _XMLStackParser() let node = try parser.parse(with: data, errorContextLength: length) @@ -22,7 +23,8 @@ class _XMLStackParser: NSObject { return node.flatten() } - func parse(with data: Data, errorContextLength: UInt) throws -> _XMLElement { + func parse(with data: Data, + errorContextLength: UInt) throws -> _XMLElement { let xmlParser = XMLParser(data: data) xmlParser.delegate = self @@ -79,12 +81,19 @@ extension _XMLStackParser: XMLParserDelegate { stack = [] } - func parser(_: XMLParser, didStartElement elementName: String, namespaceURI _: String?, qualifiedName _: String?, attributes attributeDict: [String: String] = [:]) { + func parser(_: XMLParser, + didStartElement elementName: String, + namespaceURI _: String?, + qualifiedName _: String?, + attributes attributeDict: [String: String] = [:]) { let element = _XMLElement(key: elementName, attributes: attributeDict) stack.append(element) } - func parser(_: XMLParser, didEndElement _: String, namespaceURI _: String?, qualifiedName _: String?) { + func parser(_: XMLParser, + didEndElement _: String, + namespaceURI _: String?, + qualifiedName _: String?) { guard var element = stack.popLast() else { return } diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index 0482efc9..c6e83262 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -606,6 +606,7 @@ extension _XMLDecoder { func unbox(_ box: Box) throws -> T { let decoded: T let type = T.self + if type == Date.self || type == NSDate.self { let date: Date = try unbox(box) decoded = date as! T @@ -620,9 +621,10 @@ extension _XMLDecoder { decoded = decimal as! T } else { storage.push(container: box) - defer { storage.popContainer() } - return try type.init(from: self) + decoded = try type.init(from: self) + storage.popContainer() } + return decoded } } diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift index 41794a5c..eb42fd18 100644 --- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift @@ -8,15 +8,6 @@ import Foundation -/// Type-erased protocol helper for a metatype check in generic `decode` -/// overload. -private protocol AnyEmptySequence { - init() -} - -extension Array: AnyEmptySequence {} -extension Dictionary: AnyEmptySequence {} - // MARK: Decoding Containers struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { @@ -43,7 +34,8 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol self.container = container case .convertFromSnakeCase: // Convert the snake case keys in the container to camel case. - // If we hit a duplicate key after conversion, then we'll use the first one we saw. Effectively an undefined behavior with dictionaries. + // If we hit a duplicate key after conversion, then we'll use the + // first one we saw. Effectively an undefined behavior with dictionaries. let attributes = container.attributes.map { key, value in (XMLDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value) } @@ -61,10 +53,14 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol self.container = KeyedBox(elements: elements, attributes: attributes) case let .custom(converter): let attributes = container.attributes.map { key, value in - (converter(decoder.codingPath + [_XMLKey(stringValue: key, intValue: nil)]).stringValue, value) + (converter(decoder.codingPath + + [_XMLKey(stringValue: key, intValue: nil)]).stringValue, + value) } let elements = container.elements.map { key, value in - (converter(decoder.codingPath + [_XMLKey(stringValue: key, intValue: nil)]).stringValue, value) + (converter(decoder.codingPath + + [_XMLKey(stringValue: key, intValue: nil)]).stringValue, + value) } self.container = KeyedBox(elements: elements, attributes: attributes) } @@ -113,15 +109,11 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } public func decode(_ type: Decimal.Type, forKey key: Key) throws -> Decimal { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { @@ -173,58 +165,50 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } public func decode(_ type: String.Type, forKey key: Key) throws -> String { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } public func decode(_ type: Date.Type, forKey key: Key) throws -> Date { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } public func decode(_ type: Data.Type, forKey key: Key) throws -> Data { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } public func decode(_ type: T.Type, forKey key: Key) throws -> T { let attributeNotFound = (container.attributes[key.stringValue] == nil) let elementNotFound = (container.elements[key.stringValue] == nil) - if type is AnyEmptySequence.Type, attributeNotFound, elementNotFound { - return (type as! AnyEmptySequence.Type).init() as! T + if let type = type as? AnyEmptySequence.Type, + attributeNotFound, elementNotFound { + return type.init() as! T } - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + return try decodeConcrete(type, forKey: key) } - private func decodeSignedInteger(_ type: T.Type, forKey key: Key) throws -> T { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + private func decodeSignedInteger(_ type: T.Type, + forKey key: Key) throws -> T + where T: BinaryInteger & SignedInteger & Decodable { + return try decodeConcrete(type, forKey: key) } - private func decodeUnsignedInteger(_ type: T.Type, forKey key: Key) throws -> T { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + private func decodeUnsignedInteger(_ type: T.Type, + forKey key: Key) throws -> T + where T: BinaryInteger & UnsignedInteger & Decodable { + return try decodeConcrete(type, forKey: key) } - private func decodeFloatingPoint(_ type: T.Type, forKey key: Key) throws -> T { - return try decode(type, forKey: key) { decoder, box in - try decoder.unbox(box) - } + private func decodeFloatingPoint(_ type: T.Type, + forKey key: Key) throws -> T + where T: BinaryFloatingPoint & Decodable { + return try decodeConcrete(type, forKey: key) } - private func decode( + private func decodeConcrete( _ type: T.Type, - forKey key: Key, - decode _: (_XMLDecoder, Box) throws -> T? + forKey key: Key ) throws -> T { guard let entry = container.elements[key.stringValue] ?? container.attributes[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context( @@ -236,14 +220,22 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } - guard let value: T = try decoder.unbox(entry) else { + let value: T? = try decoder.unbox(entry) + + if value == nil, + let type = type as? AnyArray.Type, + type.elementType is AnyOptional.Type { + return [nil] as! T + } + + guard let unwrapped = value else { throw DecodingError.valueNotFound(type, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "Expected \(type) value but found null instead." )) } - return value + return unwrapped } public func nestedContainer(keyedBy _: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { diff --git a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift index 80382b42..26154701 100644 --- a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift @@ -125,19 +125,22 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } } - private mutating func decodeSignedInteger(_ type: T.Type) throws -> T { + private mutating func decodeSignedInteger(_ type: T.Type) throws -> T + where T: BinaryInteger & SignedInteger & Decodable { return try decode(type) { decoder, box in try decoder.unbox(box) } } - private mutating func decodeUnsignedInteger(_ type: T.Type) throws -> T { + private mutating func decodeUnsignedInteger(_ type: T.Type) throws -> T + where T: BinaryInteger & UnsignedInteger & Decodable { return try decode(type) { decoder, box in try decoder.unbox(box) } } - private mutating func decodeFloatingPoint(_ type: T.Type) throws -> T { + private mutating func decodeFloatingPoint(_ type: T.Type) throws -> T + where T: BinaryFloatingPoint & Decodable { return try decode(type) { decoder, box in try decoder.unbox(box) } @@ -159,6 +162,13 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { let box = container[self.currentIndex] let value = try decode(decoder, box) + + defer { currentIndex += 1 } + + if value == nil, let type = type as? AnyOptional.Type { + return type.init() as! T + } + guard let decoded: T = value else { throw DecodingError.valueNotFound(type, DecodingError.Context( codingPath: decoder.codingPath + [_XMLKey(index: self.currentIndex)], @@ -166,19 +176,22 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { )) } - currentIndex += 1 return decoded } - public mutating func nestedContainer(keyedBy _: NestedKey.Type) throws -> KeyedDecodingContainer { + public mutating func nestedContainer( + keyedBy _: NestedKey.Type + ) throws -> KeyedDecodingContainer { decoder.codingPath.append(_XMLKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !isAtEnd else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context( - codingPath: codingPath, - debugDescription: "Cannot get nested keyed container -- unkeyed container is at end." - )) + throw DecodingError.valueNotFound( + KeyedDecodingContainer.self, DecodingError.Context( + codingPath: codingPath, + debugDescription: "Cannot get nested keyed container -- unkeyed container is at end." + ) + ) } let value = self.container[self.currentIndex] @@ -190,11 +203,16 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } guard let keyed = value as? KeyedBox else { - throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: value) + throw DecodingError._typeMismatch(at: codingPath, + expectation: [String: Any].self, + reality: value) } currentIndex += 1 - let container = _XMLKeyedDecodingContainer(referencing: decoder, wrapping: keyed) + let container = _XMLKeyedDecodingContainer( + referencing: decoder, + wrapping: keyed + ) return KeyedDecodingContainer(container) } @@ -203,10 +221,12 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { defer { self.decoder.codingPath.removeLast() } guard !isAtEnd else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, DecodingError.Context( - codingPath: codingPath, - debugDescription: "Cannot get nested keyed container -- unkeyed container is at end." - )) + throw DecodingError.valueNotFound( + UnkeyedDecodingContainer.self, DecodingError.Context( + codingPath: codingPath, + debugDescription: "Cannot get nested keyed container -- unkeyed container is at end." + ) + ) } let value = container[self.currentIndex] @@ -218,7 +238,9 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } guard let unkeyed = value as? UnkeyedBox else { - throw DecodingError._typeMismatch(at: codingPath, expectation: UnkeyedBox.self, reality: value) + throw DecodingError._typeMismatch(at: codingPath, + expectation: UnkeyedBox.self, + reality: value) } currentIndex += 1 @@ -238,6 +260,8 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { let value = container[self.currentIndex] currentIndex += 1 - return _XMLDecoder(referencing: value, at: decoder.codingPath, options: decoder.options) + return _XMLDecoder(referencing: value, + at: decoder.codingPath, + options: decoder.options) } } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift index 71f9e302..e4972ec0 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift @@ -23,7 +23,6 @@ class XMLElementTests: XCTestCase { XCTAssertEqual(keyed.key, "foo") XCTAssertNil(keyed.value) - debugPrint(keyed.elements) XCTAssertEqual(keyed.elements, ["foo": []]) XCTAssertEqual(keyed.attributes, [:]) } diff --git a/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift b/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift index 7d746fb4..210c913c 100644 --- a/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift +++ b/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift @@ -13,6 +13,18 @@ class UnkeyedTests: XCTestCase { let value: [String] } + struct NilContainer: Codable, Equatable { + let value: [String]? + } + + struct NestedNilContainer: Codable, Equatable { + let value: [String?] + } + + struct NilOfNilsContainer: Codable, Equatable { + let value: [String?]? + } + func testEmpty() throws { let decoder = XMLDecoder() @@ -23,6 +35,55 @@ class UnkeyedTests: XCTestCase { XCTAssertEqual(decoded.value, []) } + func testNil() throws { + let decoder = XMLDecoder() + + let xmlString = "" + let xmlData = xmlString.data(using: .utf8)! + + let decoded = try decoder.decode(NilContainer.self, from: xmlData) + XCTAssertEqual(decoded.value, nil) + } + + func testNilNil() throws { + let decoder = XMLDecoder() + + let xmlString = "" + let xmlData = xmlString.data(using: .utf8)! + + let decoded = try decoder.decode(NilOfNilsContainer.self, + from: xmlData) + XCTAssertEqual(decoded.value, nil) + } + + func testNestedNilMultiElement() throws { + let decoder = XMLDecoder() + + let xmlData = """ + + test1 + + test2 + + """.data(using: .utf8)! + + let decoded = try decoder.decode(NestedNilContainer.self, from: xmlData) + XCTAssertEqual(decoded.value, ["test1", nil, "test2"]) + } + + func testNestedNilSingleElement() throws { + let decoder = XMLDecoder() + + let xmlData = """ + + + + """.data(using: .utf8)! + + let decoded = try decoder.decode(NestedNilContainer.self, from: xmlData) + XCTAssertEqual(decoded.value, [nil]) + } + func testSingleElement() throws { let decoder = XMLDecoder() diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index d8611d1f..5c8bb7f2 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ BF9457F621CBB6BC005ACFDE /* KeyedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EB21CBB6BC005ACFDE /* KeyedTests.swift */; }; BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EC21CBB6BC005ACFDE /* DataTests.swift */; }; D1E0C85321D8E65E0042A261 /* ErrorContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85121D8E6540042A261 /* ErrorContextTest.swift */; }; + D1E0C85521D91EBF0042A261 /* Metatypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85421D91EBF0042A261 /* Metatypes.swift */; }; D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1FC040421C7EF8200065B43 /* RJISample.swift */; }; OBJ_48 /* DecodingErrorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* DecodingErrorExtension.swift */; }; OBJ_49 /* XMLDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* XMLDecoder.swift */; }; @@ -163,6 +164,7 @@ 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 = ""; }; D1E0C85121D8E6540042A261 /* ErrorContextTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorContextTest.swift; sourceTree = ""; }; + D1E0C85421D91EBF0042A261 /* Metatypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metatypes.swift; sourceTree = ""; }; D1FC040421C7EF8200065B43 /* RJISample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RJISample.swift; sourceTree = ""; }; OBJ_10 /* DecodingErrorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodingErrorExtension.swift; sourceTree = ""; }; OBJ_11 /* XMLDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLDecoder.swift; sourceTree = ""; }; @@ -250,6 +252,7 @@ BF9457B421CBB4DB005ACFDE /* ISO8601DateFormatter.swift */, BF9457B521CBB4DB005ACFDE /* XMLKey.swift */, BF9457B621CBB4DB005ACFDE /* XMLElement.swift */, + D1E0C85421D91EBF0042A261 /* Metatypes.swift */, ); path = Auxiliaries; sourceTree = ""; @@ -498,6 +501,7 @@ BF9457DA21CBB5D2005ACFDE /* DataBox.swift in Sources */, BF9457AB21CBB498005ACFDE /* DecimalBox.swift in Sources */, OBJ_56 /* XMLKeyedEncodingContainer.swift in Sources */, + D1E0C85521D91EBF0042A261 /* Metatypes.swift in Sources */, OBJ_57 /* XMLReferencingEncoder.swift in Sources */, BF9457BC21CBB4DB005ACFDE /* XMLElement.swift in Sources */, BF9457AA21CBB498005ACFDE /* UnkeyedBox.swift in Sources */, From 3cf31e2bc7d9655d7fe371f37d7908bdaeb4b1a8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 30 Dec 2018 16:45:20 +0000 Subject: [PATCH 2/3] Rename header comment in Metatypes file --- Sources/XMLCoder/Auxiliaries/Metatypes.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XMLCoder/Auxiliaries/Metatypes.swift b/Sources/XMLCoder/Auxiliaries/Metatypes.swift index ea0263d9..46c3c240 100644 --- a/Sources/XMLCoder/Auxiliaries/Metatypes.swift +++ b/Sources/XMLCoder/Auxiliaries/Metatypes.swift @@ -1,5 +1,5 @@ // -// TypeErasure.swift +// Metatypes.swift // XMLCoder // // Created by Max Desiatov on 30/12/2018. From 5a273572a1b38a8b0f59b85d58c6da502ce5e80a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 30 Dec 2018 16:49:21 +0000 Subject: [PATCH 3/3] Fix formatting --- Sources/XMLCoder/Auxiliaries/Metatypes.swift | 1 + Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/XMLCoder/Auxiliaries/Metatypes.swift b/Sources/XMLCoder/Auxiliaries/Metatypes.swift index 46c3c240..69afea9f 100644 --- a/Sources/XMLCoder/Auxiliaries/Metatypes.swift +++ b/Sources/XMLCoder/Auxiliaries/Metatypes.swift @@ -20,6 +20,7 @@ extension Array: AnyEmptySequence, AnyArray { return Element.self } } + extension Dictionary: AnyEmptySequence {} /// Type-erased protocol helper for a metatype check in generic `decode` diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift index eb42fd18..73f97cb7 100644 --- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift @@ -223,8 +223,8 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol let value: T? = try decoder.unbox(entry) if value == nil, - let type = type as? AnyArray.Type, - type.elementType is AnyOptional.Type { + let type = type as? AnyArray.Type, + type.elementType is AnyOptional.Type { return [nil] as! T }