From 3ca321a7508d6a00999e7eac99c249d3b8ae6ee8 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 20 Nov 2018 11:40:52 +0100 Subject: [PATCH 1/6] Moved `_XML(Unk|K)eyedEncodingContainer` into their own files, matching decoder --- Sources/XMLCoder/Encoder/XMLEncoder.swift | 505 +----------------- .../Encoder/XMLKeyedEncodingContainer.swift | 376 +++++++++++++ .../Encoder/XMLUnkeyedEncodingContainer.swift | 95 ++++ XMLCoder.xcodeproj/project.pbxproj | 8 + 4 files changed, 499 insertions(+), 485 deletions(-) create mode 100644 Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift create mode 100644 Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift diff --git a/Sources/XMLCoder/Encoder/XMLEncoder.swift b/Sources/XMLCoder/Encoder/XMLEncoder.swift index 879f0fb3..2343539f 100644 --- a/Sources/XMLCoder/Encoder/XMLEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLEncoder.swift @@ -374,476 +374,11 @@ internal class _XMLEncoder: Encoder { } } -// MARK: - Encoding Containers - -fileprivate struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: _XMLEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableDictionary - - /// The path of coding keys taken to get to this point in encoding. - public private(set) var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - Coding Path Operations - - private func _converted(_ key: CodingKey) -> CodingKey { - switch encoder.options.keyEncodingStrategy { - case .useDefaultKeys: - return key - case .convertToSnakeCase: - let newKeyString = XMLEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue) - return _XMLKey(stringValue: newKeyString, intValue: key.intValue) - case let .custom(converter): - return converter(codingPath + [key]) - } - } - - // MARK: - KeyedEncodingContainerProtocol Methods - - public mutating func encodeNil(forKey key: Key) throws { - container[_converted(key).stringValue] = NSNull() - } - - public mutating func encode(_ value: Bool, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Int, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Int8, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Int16, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Int32, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Int64, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: UInt, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: UInt8, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: UInt16, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: UInt32, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: UInt64, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: String, forKey key: Key) throws { - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = encoder.box(value) - } - } - - public mutating func encode(_ value: Float, forKey key: Key) throws { - // Since the float may be invalid and throw, the coding path needs to contain this key. - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - container[_converted(key).stringValue] = try encoder.box(value) - } - - public mutating func encode(_ value: Double, forKey key: Key) throws { - // Since the double may be invalid and throw, the coding path needs to contain this key. - encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = try encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = try encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = try encoder.box(value) - } - } - - public mutating func encode(_ value: T, forKey key: Key) throws { - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - encoder.codingPath.append(key) - let nodeEncodings = encoder.options.nodeEncodingStrategy.nodeEncodings( - forType: T.self, - with: encoder - ) - encoder.nodeEncodings.append(nodeEncodings) - defer { - _ = self.encoder.nodeEncodings.removeLast() - self.encoder.codingPath.removeLast() - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = try encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = try encoder.box(value) - container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - container[_converted(key).stringValue] = try encoder.box(value) - } - } - - public mutating func nestedContainer(keyedBy _: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - let dictionary = NSMutableDictionary() - self.container[_converted(key).stringValue] = dictionary - - codingPath.append(key) - defer { self.codingPath.removeLast() } - - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let array = NSMutableArray() - container[_converted(key).stringValue] = array - - codingPath.append(key) - defer { self.codingPath.removeLast() } - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, key: _XMLKey.super, convertedKey: _converted(_XMLKey.super), wrapping: container) - } - - public mutating func superEncoder(forKey key: Key) -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, key: key, convertedKey: _converted(key), wrapping: container) - } -} - -fileprivate struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: _XMLEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableArray - - /// The path of coding keys taken to get to this point in encoding. - public private(set) var codingPath: [CodingKey] - - /// The number of elements encoded into the container. - public var count: Int { - return container.count - } - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - UnkeyedEncodingContainer Methods - - public mutating func encodeNil() throws { container.add(NSNull()) } - public mutating func encode(_ value: Bool) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: Int) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: Int8) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: Int16) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: Int32) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: Int64) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: UInt) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: UInt8) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: UInt16) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: UInt32) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: UInt64) throws { container.add(encoder.box(value)) } - public mutating func encode(_ value: String) throws { container.add(encoder.box(value)) } - - public mutating func encode(_ value: Float) throws { - // Since the float may be invalid and throw, the coding path needs to contain this key. - encoder.codingPath.append(_XMLKey(index: count)) - defer { self.encoder.codingPath.removeLast() } - container.add(try encoder.box(value)) - } - - public mutating func encode(_ value: Double) throws { - // Since the double may be invalid and throw, the coding path needs to contain this key. - encoder.codingPath.append(_XMLKey(index: count)) - defer { self.encoder.codingPath.removeLast() } - container.add(try encoder.box(value)) - } - - public mutating func encode(_ value: T) throws { - encoder.codingPath.append(_XMLKey(index: count)) - let nodeEncodings = encoder.options.nodeEncodingStrategy.nodeEncodings( - forType: T.self, - with: encoder - ) - encoder.nodeEncodings.append(nodeEncodings) - defer { - _ = self.encoder.nodeEncodings.removeLast() - self.encoder.codingPath.removeLast() - } - container.add(try encoder.box(value)) - } - - public mutating func nestedContainer(keyedBy _: NestedKey.Type) -> KeyedEncodingContainer { - codingPath.append(_XMLKey(index: count)) - defer { self.codingPath.removeLast() } - - let dictionary = NSMutableDictionary() - self.container.add(dictionary) - - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - codingPath.append(_XMLKey(index: count)) - defer { self.codingPath.removeLast() } - - let array = NSMutableArray() - container.add(array) - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, at: container.count, wrapping: container) - } -} - extension _XMLEncoder: SingleValueEncodingContainer { // MARK: - SingleValueEncodingContainer Methods - - fileprivate func assertCanEncodeNewValue() { - precondition(canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") + + internal func assertCanEncodeNewValue() { + precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") } public func encodeNil() throws { @@ -929,19 +464,19 @@ extension _XMLEncoder: SingleValueEncodingContainer { extension _XMLEncoder { /// Returns the given value boxed in a container appropriate for pushing onto the container stack. - fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) } - + internal func box(_ value: Bool) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: Int) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: Int8) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: Int16) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: Int32) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: Int64) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: UInt) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) } + internal func box(_ value: String) -> NSObject { return NSString(string: value) } + internal func box(_ value: Float) throws -> NSObject { if value.isInfinite || value.isNaN { guard case let .convertToString(positiveInfinity: posInfString, negativeInfinity: negInfString, nan: nanString) = options.nonConformingFloatEncodingStrategy else { @@ -1021,13 +556,13 @@ extension _XMLEncoder { return storage.popContainer() as NSObject } } - - fileprivate func box(_ value: T) throws -> NSObject { - return try box_(value) ?? NSDictionary() + + internal func box(_ value: T) throws -> NSObject { + return try self.box_(value) ?? NSDictionary() } // This method is called "box_" instead of "box" to disambiguate it from the overloads. Because the return type here is different from all of the "box" overloads (and is more general), any "box" calls in here would call back into "box" recursively instead of calling the appropriate overload, which is not what we want. - fileprivate func box_(_ value: T) throws -> NSObject? { + internal func box_(_ value: T) throws -> NSObject? { if T.self == Date.self || T.self == NSDate.self { return try box((value as! Date)) } else if T.self == Data.self || T.self == NSData.self { diff --git a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift new file mode 100644 index 00000000..1037fcda --- /dev/null +++ b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift @@ -0,0 +1,376 @@ +// +// XMLKeyedEncodingContainer.swift +// XMLCoder +// +// Created by Vincent Esche on 11/20/18. +// + +import Foundation + +internal struct _XMLKeyedEncodingContainer : KeyedEncodingContainerProtocol { + typealias Key = K + + // MARK: Properties + + /// A reference to the encoder we're writing to. + private let encoder: _XMLEncoder + + /// A reference to the container we're writing to. + private let container: NSMutableDictionary + + /// The path of coding keys taken to get to this point in encoding. + private(set) public var codingPath: [CodingKey] + + // MARK: - Initialization + + /// Initializes `self` with the given references. + internal init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) { + self.encoder = encoder + self.codingPath = codingPath + self.container = container + } + + // MARK: - Coding Path Operations + + private func _converted(_ key: CodingKey) -> CodingKey { + switch encoder.options.keyEncodingStrategy { + case .useDefaultKeys: + return key + case .convertToSnakeCase: + let newKeyString = XMLEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue) + return _XMLKey(stringValue: newKeyString, intValue: key.intValue) + case .custom(let converter): + return converter(codingPath + [key]) + } + } + + // MARK: - KeyedEncodingContainerProtocol Methods + + public mutating func encodeNil(forKey key: Key) throws { + self.container[_converted(key).stringValue] = NSNull() + } + + public mutating func encode(_ value: Bool, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Int, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Int8, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Int16, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Int32, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Int64, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: UInt, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: UInt8, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: UInt16, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: UInt32, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: UInt64, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: String, forKey key: Key) throws { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = self.encoder.box(value) + } + } + + public mutating func encode(_ value: Float, forKey key: Key) throws { + // Since the float may be invalid and throw, the coding path needs to contain this key. + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + self.container[_converted(key).stringValue] = try self.encoder.box(value) + } + + public mutating func encode(_ value: Double, forKey key: Key) throws { + // Since the double may be invalid and throw, the coding path needs to contain this key. + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = try self.encoder.box(value) + } + } + + public mutating func encode(_ value: T, forKey key: Key) throws { + guard let strategy = self.encoder.nodeEncodings.last else { + preconditionFailure("Attempt to access node encoding strategy from empty stack.") + } + self.encoder.codingPath.append(key) + let nodeEncodings = self.encoder.options.nodeEncodingStrategy.nodeEncodings( + forType: T.self, + with: self.encoder + ) + self.encoder.nodeEncodings.append(nodeEncodings) + defer { + let _ = self.encoder.nodeEncodings.removeLast() + self.encoder.codingPath.removeLast() + } + switch strategy(key) { + case .attribute: + if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) + } else { + let attributesContainer = NSMutableDictionary() + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) + self.container[_XMLElement.attributesKey] = attributesContainer + } + case .element: + self.container[_converted(key).stringValue] = try self.encoder.box(value) + } + } + + public mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + let dictionary = NSMutableDictionary() + self.container[_converted(key).stringValue] = dictionary + + self.codingPath.append(key) + defer { self.codingPath.removeLast() } + + let container = _XMLKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) + return KeyedEncodingContainer(container) + } + + public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + let array = NSMutableArray() + self.container[_converted(key).stringValue] = array + + self.codingPath.append(key) + defer { self.codingPath.removeLast() } + return _XMLUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) + } + + public mutating func superEncoder() -> Encoder { + return _XMLReferencingEncoder(referencing: self.encoder, key: _XMLKey.super, convertedKey: _converted(_XMLKey.super), wrapping: self.container) + } + + public mutating func superEncoder(forKey key: Key) -> Encoder { + return _XMLReferencingEncoder(referencing: self.encoder, key: key, convertedKey: _converted(key), wrapping: self.container) + } +} diff --git a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift new file mode 100644 index 00000000..88c83139 --- /dev/null +++ b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift @@ -0,0 +1,95 @@ +// +// XMLUnkeyedEncodingContainer.swift +// XMLCoder +// +// Created by Vincent Esche on 11/20/18. +// + +import Foundation + +internal struct _XMLUnkeyedEncodingContainer : UnkeyedEncodingContainer { + // MARK: Properties + + /// A reference to the encoder we're writing to. + private let encoder: _XMLEncoder + + /// A reference to the container we're writing to. + private let container: NSMutableArray + + /// The path of coding keys taken to get to this point in encoding. + private(set) public var codingPath: [CodingKey] + + /// The number of elements encoded into the container. + public var count: Int { + return self.container.count + } + + // MARK: - Initialization + + /// Initializes `self` with the given references. + internal init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) { + self.encoder = encoder + self.codingPath = codingPath + self.container = container + } + + // MARK: - UnkeyedEncodingContainer Methods + + public mutating func encodeNil() throws { self.container.add(NSNull()) } + public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) } + public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) } + + public mutating func encode(_ value: Float) throws { + // Since the float may be invalid and throw, the coding path needs to contain this key. + self.encoder.codingPath.append(_XMLKey(index: self.count)) + defer { self.encoder.codingPath.removeLast() } + self.container.add(try self.encoder.box(value)) + } + + public mutating func encode(_ value: Double) throws { + // Since the double may be invalid and throw, the coding path needs to contain this key. + self.encoder.codingPath.append(_XMLKey(index: self.count)) + defer { self.encoder.codingPath.removeLast() } + self.container.add(try self.encoder.box(value)) + } + + public mutating func encode(_ value: T) throws { + self.encoder.codingPath.append(_XMLKey(index: self.count)) + defer { self.encoder.codingPath.removeLast() } + self.container.add(try self.encoder.box(value)) + } + + public mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { + self.codingPath.append(_XMLKey(index: self.count)) + defer { self.codingPath.removeLast() } + + let dictionary = NSMutableDictionary() + self.container.add(dictionary) + + let container = _XMLKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) + return KeyedEncodingContainer(container) + } + + public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + self.codingPath.append(_XMLKey(index: self.count)) + defer { self.codingPath.removeLast() } + + let array = NSMutableArray() + self.container.add(array) + return _XMLUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) + } + + public mutating func superEncoder() -> Encoder { + return _XMLReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container) + } +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 1163a757..fb86b3b6 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ BF191DE621A467F600EAB791 /* RJITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF191DE421A467F600EAB791 /* RJITest.swift */; }; BF191DE821A467FF00EAB791 /* RJITest.xml in Resources */ = {isa = PBXBuildFile; fileRef = BF191DE521A467F600EAB791 /* RJITest.xml */; }; BFE1C58E21A4203200EA0458 /* NoteTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE1C58D21A4203200EA0458 /* NoteTest.swift */; }; + BFE1C58521A41AFB00EA0458 /* XMLUnkeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE1C58421A41AFA00EA0458 /* XMLUnkeyedEncodingContainer.swift */; }; + BFE1C58721A41B3200EA0458 /* XMLKeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE1C58621A41B3200EA0458 /* XMLKeyedEncodingContainer.swift */; }; BFE1C59121A4242300EA0458 /* NodeEncodingStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE1C58F21A4232100EA0458 /* NodeEncodingStrategyTests.swift */; }; D15ACF3F21A2C97F003238FD /* BreakfastTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D15ACF3E21A2C97F003238FD /* BreakfastTest.swift */; }; D15ACF4221A407AA003238FD /* BooksTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D15ACF3321A2C7F2003238FD /* BooksTest.swift */; }; @@ -69,6 +71,8 @@ BF191DE421A467F600EAB791 /* RJITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RJITest.swift; sourceTree = ""; }; BF191DE521A467F600EAB791 /* RJITest.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = RJITest.xml; sourceTree = ""; }; BFE1C58D21A4203200EA0458 /* NoteTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoteTest.swift; sourceTree = ""; }; + BFE1C58421A41AFA00EA0458 /* XMLUnkeyedEncodingContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = XMLUnkeyedEncodingContainer.swift; sourceTree = ""; tabWidth = 4; }; + BFE1C58621A41B3200EA0458 /* XMLKeyedEncodingContainer.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = XMLKeyedEncodingContainer.swift; sourceTree = ""; tabWidth = 4; }; BFE1C58F21A4232100EA0458 /* NodeEncodingStrategyTests.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = NodeEncodingStrategyTests.swift; sourceTree = ""; tabWidth = 4; }; D15ACF3321A2C7F2003238FD /* BooksTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooksTest.swift; sourceTree = ""; }; D15ACF3E21A2C97F003238FD /* BreakfastTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreakfastTest.swift; sourceTree = ""; }; @@ -125,6 +129,8 @@ OBJ_16 /* EncodingErrorExtension.swift */, OBJ_17 /* XMLEncoder.swift */, OBJ_18 /* XMLEncodingStorage.swift */, + BFE1C58621A41B3200EA0458 /* XMLKeyedEncodingContainer.swift */, + BFE1C58421A41AFA00EA0458 /* XMLUnkeyedEncodingContainer.swift */, OBJ_19 /* XMLReferencingEncoder.swift */, ); path = Encoder; @@ -350,6 +356,7 @@ OBJ_50 /* DecodingErrorExtension.swift in Sources */, OBJ_51 /* XMLDecoder.swift in Sources */, OBJ_52 /* XMLDecodingStorage.swift in Sources */, + BFE1C58521A41AFB00EA0458 /* XMLUnkeyedEncodingContainer.swift in Sources */, OBJ_53 /* XMLKeyedDecodingContainer.swift in Sources */, OBJ_54 /* XMLUnkeyedDecodingContainer.swift in Sources */, OBJ_55 /* EncodingErrorExtension.swift in Sources */, @@ -357,6 +364,7 @@ OBJ_57 /* XMLEncodingStorage.swift in Sources */, OBJ_58 /* XMLReferencingEncoder.swift in Sources */, OBJ_59 /* ISO8601DateFormatter.swift in Sources */, + BFE1C58721A41B3200EA0458 /* XMLKeyedEncodingContainer.swift in Sources */, OBJ_60 /* XMLKey.swift in Sources */, OBJ_61 /* XMLStackParser.swift in Sources */, ); From f89d1609355016f1b2dce24c77deb5728d6e2071 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 14 Dec 2018 10:44:16 +0000 Subject: [PATCH 2/6] Fix `testUnkeyedContainer` in NodeEncodingStrategy --- Tests/XMLCoderTests/NodeEncodingStrategyTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift index 0b273ad0..0b95b786 100644 --- a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift +++ b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift @@ -186,10 +186,10 @@ final class NodeEncodingStrategyTests: XCTestCase { } encoder.nodeEncodingStrategy = .custom { codableType, _ in - guard let barType = codableType as? Element.Type else { + guard codableType is [Element].Type else { return { _ in .default } } - return barType.nodeEncoding(forKey:) + return Element.nodeEncoding(forKey:) } do { From 427e4fd0aaec63f886f4b855f8f2eb20c2c2a96d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 14 Dec 2018 11:43:37 +0000 Subject: [PATCH 3/6] Add Int key to NodeEncodingStrategyTests --- .../Encoder/EncodingErrorExtension.swift | 4 +- Sources/XMLCoder/ISO8601DateFormatter.swift | 10 +- Sources/XMLCoder/XMLKey.swift | 9 +- Tests/LinuxMain.swift | 2 +- .../NodeEncodingStrategyTests.swift | 125 ++++++++++-------- 5 files changed, 82 insertions(+), 68 deletions(-) diff --git a/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift b/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift index f2b7eebe..077f0218 100644 --- a/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift +++ b/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift @@ -8,9 +8,7 @@ import Foundation -//===----------------------------------------------------------------------===// -// Error Utilities -//===----------------------------------------------------------------------===// +/// Error Utilities internal extension EncodingError { /// Returns a `.invalidValue` error describing the given invalid floating-point value. /// diff --git a/Sources/XMLCoder/ISO8601DateFormatter.swift b/Sources/XMLCoder/ISO8601DateFormatter.swift index f8e05ae5..023c88ef 100644 --- a/Sources/XMLCoder/ISO8601DateFormatter.swift +++ b/Sources/XMLCoder/ISO8601DateFormatter.swift @@ -8,11 +8,11 @@ import Foundation -//===----------------------------------------------------------------------===// -// Shared ISO8601 Date Formatter -//===----------------------------------------------------------------------===// - -// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS. +/// Shared ISO8601 Date Formatter +/// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled +/// against the latest SDK (w/ ISO8601DateFormatter), but linked against +/// whichever Foundation the user has. ISO8601DateFormatter might not exist, so +/// we better not hit this code path on an older OS. @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) internal var _iso8601Formatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() diff --git a/Sources/XMLCoder/XMLKey.swift b/Sources/XMLCoder/XMLKey.swift index 0030ebd6..44bf33d1 100644 --- a/Sources/XMLCoder/XMLKey.swift +++ b/Sources/XMLCoder/XMLKey.swift @@ -8,13 +8,10 @@ import Foundation -//===----------------------------------------------------------------------===// -// Shared Key Types -//===----------------------------------------------------------------------===// - +/// Shared Key Types internal struct _XMLKey: CodingKey { - public var stringValue: String - public var intValue: Int? + public let stringValue: String + public let intValue: Int? public init?(stringValue: String) { self.stringValue = stringValue diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 2dd62498..da995d32 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -6,6 +6,6 @@ XCTMain([ testCase(BreakfastTest.allTests), testCase(NodeEncodingStrategyTests.allTests), testCase(BooksTest.allTests), - testCase(NoteTest.allTests) + testCase(NoteTest.allTests), testCase(PlantTest.allTests), ]) diff --git a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift index 0b95b786..2c7d2b49 100644 --- a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift +++ b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift @@ -1,72 +1,79 @@ import XCTest @testable import XMLCoder -final class NodeEncodingStrategyTests: XCTestCase { - fileprivate struct SingleContainer: Encodable { - let element: Element +fileprivate struct SingleContainer: Encodable { + let element: Element - enum CodingKeys: String, CodingKey { - case element - } + enum CodingKeys: String, CodingKey { + case element } +} - fileprivate struct KeyedContainer: Encodable { - let elements: [String: Element] +fileprivate struct KeyedContainer: Encodable { + let elements: [String: Element] - enum CodingKeys: String, CodingKey { - case elements = "element" - } + enum CodingKeys: String, CodingKey { + case elements = "element" } +} - fileprivate struct UnkeyedContainer: Encodable { - let elements: [Element] +fileprivate struct UnkeyedContainer: Encodable { + let elements: [Element] - enum CodingKeys: String, CodingKey { - case elements = "element" - } + enum CodingKeys: String, CodingKey { + case elements = "element" } +} - fileprivate struct Element: Encodable { - let key: String = "value" +fileprivate struct Element: Encodable { + let key: String = "value" + let intKey: Int = 42 - enum CodingKeys: CodingKey { - case key - } + enum CodingKeys: CodingKey { + case key + case intKey + } - static func nodeEncoding(forKey _: CodingKey) -> XMLEncoder.NodeEncoding { - return .attribute - } + static func nodeEncoding(forKey _: CodingKey) -> XMLEncoder.NodeEncoding { + return .attribute } +} - fileprivate struct ComplexUnkeyedContainer: Encodable { - let elements: [ComplexElement] +fileprivate struct ComplexUnkeyedContainer: Encodable { + let elements: [ComplexElement] - enum CodingKeys: String, CodingKey { - case elements = "element" - } + enum CodingKeys: String, CodingKey { + case elements = "element" } +} - fileprivate struct ComplexElement: Encodable { - struct Key: Encodable { - let a: String - let b: String - let c: String - } +fileprivate struct ComplexElement: Encodable { + struct Key: Encodable { + let a: String + let b: String + let c: String + } - var key: Key = Key(a: "C", b: "B", c: "A") + var key: Key = Key(a: "C", b: "B", c: "A") - enum CodingKeys: CodingKey { - case key - } + enum CodingKeys: CodingKey { + case key + } - static func nodeEncoding(forKey _: CodingKey) -> XMLEncoder.NodeEncoding { - return .attribute - } + static func nodeEncoding(forKey _: CodingKey) -> XMLEncoder.NodeEncoding { + return .attribute } +} +final class NodeEncodingStrategyTests: XCTestCase { func testSingleContainer() { + guard #available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) else { + return + } + let encoder = XMLEncoder() - encoder.outputFormatting = .prettyPrinted + + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] do { let container = SingleContainer(element: Element()) @@ -77,6 +84,7 @@ final class NodeEncodingStrategyTests: XCTestCase { """ + 42 value @@ -101,7 +109,7 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - + """ XCTAssertEqual(xml, expected) @@ -111,8 +119,12 @@ final class NodeEncodingStrategyTests: XCTestCase { } func testKeyedContainer() { + guard #available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) else { + return + } + let encoder = XMLEncoder() - encoder.outputFormatting = .prettyPrinted + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] do { let container = KeyedContainer(elements: ["first": Element()]) @@ -124,6 +136,7 @@ final class NodeEncodingStrategyTests: XCTestCase { + 42 value @@ -150,7 +163,7 @@ final class NodeEncodingStrategyTests: XCTestCase { """ - + """ @@ -161,8 +174,12 @@ final class NodeEncodingStrategyTests: XCTestCase { } func testUnkeyedContainer() { + guard #available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) else { + return + } + let encoder = XMLEncoder() - encoder.outputFormatting = .prettyPrinted + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] do { let container = UnkeyedContainer(elements: [Element(), Element()]) @@ -173,9 +190,11 @@ final class NodeEncodingStrategyTests: XCTestCase { """ + 42 value + 42 value @@ -200,8 +219,8 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - - + + """ XCTAssertEqual(xml, expected) @@ -217,13 +236,13 @@ final class NodeEncodingStrategyTests: XCTestCase { ] func testItSortsKeysWhenEncodingAsElements() { - let encoder = XMLEncoder() - if #available(macOS 10.13, *) { - encoder.outputFormatting = [.sortedKeys, .prettyPrinted] - } else { + guard #available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) else { return } + let encoder = XMLEncoder() + encoder.outputFormatting = [.sortedKeys, .prettyPrinted] + do { let container = ComplexUnkeyedContainer(elements: [ComplexElement()]) let data = try encoder.encode(container, withRootKey: "container") From e06df5417cfb98a2836beaa47f54804c57238848 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 14 Dec 2018 11:51:42 +0000 Subject: [PATCH 4/6] Increase XMLKeyedEncodingContainer test coverage --- .../XMLCoderTests/NodeEncodingStrategyTests.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift index 2c7d2b49..600d3f14 100644 --- a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift +++ b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift @@ -28,10 +28,12 @@ fileprivate struct UnkeyedContainer: Encodable { fileprivate struct Element: Encodable { let key: String = "value" let intKey: Int = 42 + let doubleKey: Double = 42.42 enum CodingKeys: CodingKey { case key case intKey + case doubleKey } static func nodeEncoding(forKey _: CodingKey) -> XMLEncoder.NodeEncoding { @@ -84,6 +86,7 @@ final class NodeEncodingStrategyTests: XCTestCase { """ + 42.42 42 value @@ -109,7 +112,7 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - + """ XCTAssertEqual(xml, expected) @@ -136,6 +139,7 @@ final class NodeEncodingStrategyTests: XCTestCase { + 42.42 42 value @@ -163,7 +167,7 @@ final class NodeEncodingStrategyTests: XCTestCase { """ - + """ @@ -190,10 +194,12 @@ final class NodeEncodingStrategyTests: XCTestCase { """ + 42.42 42 value + 42.42 42 value @@ -219,8 +225,8 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - - + + """ XCTAssertEqual(xml, expected) From 3d16865ff0641dbc19db93c5ed980f918c0995da Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 14 Dec 2018 12:05:55 +0000 Subject: [PATCH 5/6] Cleanup integers in XMLKeyedEncodingContainer --- .../Encoder/XMLKeyedEncodingContainer.swift | 188 +----------------- .../NodeEncodingStrategyTests.swift | 14 +- 2 files changed, 14 insertions(+), 188 deletions(-) diff --git a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift index 1037fcda..6de04ac7 100644 --- a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift @@ -70,7 +70,7 @@ internal struct _XMLKeyedEncodingContainer : KeyedEncodingContain } } - public mutating func encode(_ value: Int, forKey key: Key) throws { + public mutating func encode(_ value: T, forKey key: Key) throws { self.encoder.codingPath.append(key) defer { self.encoder.codingPath.removeLast() } guard let strategy = self.encoder.nodeEncodings.last else { @@ -79,194 +79,14 @@ internal struct _XMLKeyedEncodingContainer : KeyedEncodingContain switch strategy(key) { case .attribute: if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: Int8, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: Int16, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: Int32, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: Int64, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: UInt, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: UInt8, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: UInt16, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: UInt32, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - } else { - let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) - self.container[_XMLElement.attributesKey] = attributesContainer - } - case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - } - - public mutating func encode(_ value: UInt64, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") - } - switch strategy(key) { - case .attribute: - if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary { - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) } else { let attributesContainer = NSMutableDictionary() - attributesContainer[_converted(key).stringValue] = self.encoder.box(value) + attributesContainer[_converted(key).stringValue] = try self.encoder.box(value) self.container[_XMLElement.attributesKey] = attributesContainer } case .element: - self.container[_converted(key).stringValue] = self.encoder.box(value) + self.container[_converted(key).stringValue] = try self.encoder.box(value) } } diff --git a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift index 600d3f14..f5ad503d 100644 --- a/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift +++ b/Tests/XMLCoderTests/NodeEncodingStrategyTests.swift @@ -28,11 +28,13 @@ fileprivate struct UnkeyedContainer: Encodable { fileprivate struct Element: Encodable { let key: String = "value" let intKey: Int = 42 + let int8Key: Int8 = 42 let doubleKey: Double = 42.42 enum CodingKeys: CodingKey { case key case intKey + case int8Key case doubleKey } @@ -87,6 +89,7 @@ final class NodeEncodingStrategyTests: XCTestCase { 42.42 + 42 42 value @@ -112,7 +115,7 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - + """ XCTAssertEqual(xml, expected) @@ -140,6 +143,7 @@ final class NodeEncodingStrategyTests: XCTestCase { 42.42 + 42 42 value @@ -167,7 +171,7 @@ final class NodeEncodingStrategyTests: XCTestCase { """ - + """ @@ -195,11 +199,13 @@ final class NodeEncodingStrategyTests: XCTestCase { 42.42 + 42 42 value 42.42 + 42 42 value @@ -225,8 +231,8 @@ final class NodeEncodingStrategyTests: XCTestCase { let expected = """ - - + + """ XCTAssertEqual(xml, expected) From 17356014695d50b5f8cb1471a8478885bfd97265 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 14 Dec 2018 12:12:09 +0000 Subject: [PATCH 6/6] Cleanup integers in XMLUnkeyedEncodingContainer --- .../Encoder/XMLUnkeyedEncodingContainer.swift | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift index 88c83139..22c24c65 100644 --- a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift @@ -37,16 +37,11 @@ internal struct _XMLUnkeyedEncodingContainer : UnkeyedEncodingContainer { public mutating func encodeNil() throws { self.container.add(NSNull()) } public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) } + + public mutating func encode(_ value: T) throws { + try self.container.add(self.encoder.box(value)) + } + public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) } public mutating func encode(_ value: Float) throws {