Skip to content

Commit fc304c5

Browse files
committed
feat: support root attributes propagation
1 parent ae6a0f0 commit fc304c5

File tree

2 files changed

+28
-24
lines changed

2 files changed

+28
-24
lines changed

Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -293,21 +293,23 @@ struct XMLCoderElement: Equatable {
293293
// MARK: - Convenience Initializers
294294

295295
extension XMLCoderElement {
296-
init(key: String, box: UnkeyedBox) {
296+
init(key: String, box: UnkeyedBox, attributes: [Attribute] = []) {
297297
if let containsChoice = box as? [ChoiceBox] {
298-
self.init(key: key, elements: containsChoice.map {
299-
XMLCoderElement(key: $0.key, box: $0.element)
300-
})
298+
self.init(
299+
key: key,
300+
elements: containsChoice.map { XMLCoderElement(key: $0.key, box: $0.element) },
301+
attributes: attributes
302+
)
301303
} else {
302-
self.init(key: key, elements: box.map { XMLCoderElement(key: key, box: $0) })
304+
self.init(key: key, elements: box.map { XMLCoderElement(key: key, box: $0) }, attributes: attributes)
303305
}
304306
}
305307

306-
init(key: String, box: ChoiceBox) {
307-
self.init(key: key, elements: [XMLCoderElement(key: box.key, box: box.element)])
308+
init(key: String, box: ChoiceBox, attributes: [Attribute] = []) {
309+
self.init(key: key, elements: [XMLCoderElement(key: box.key, box: box.element)], attributes: attributes)
308310
}
309311

310-
init(key: String, box: KeyedBox) {
312+
init(key: String, box: KeyedBox, attributes: [Attribute] = []) {
311313
var elements: [XMLCoderElement] = []
312314

313315
for (key, box) in box.elements {
@@ -338,7 +340,7 @@ extension XMLCoderElement {
338340
}
339341
}
340342

341-
let attributes: [Attribute] = box.attributes.compactMap { key, box in
343+
let attributes: [Attribute] = attributes + box.attributes.compactMap { key, box in
342344
guard let value = box.xmlString else {
343345
return nil
344346
}
@@ -348,30 +350,30 @@ extension XMLCoderElement {
348350
self.init(key: key, elements: elements, attributes: attributes)
349351
}
350352

351-
init(key: String, box: SimpleBox) {
353+
init(key: String, box: SimpleBox, attributes: [Attribute] = []) {
352354
if let value = box.xmlString {
353-
self.init(key: key, stringValue: value)
355+
self.init(key: key, stringValue: value, attributes: attributes)
354356
} else {
355-
self.init(key: key)
357+
self.init(key: key, attributes: attributes)
356358
}
357359
}
358360

359-
init(key: String, box: Box) {
361+
init(key: String, box: Box, attributes: [Attribute] = []) {
360362
switch box {
361363
case let sharedUnkeyedBox as SharedBox<UnkeyedBox>:
362-
self.init(key: key, box: sharedUnkeyedBox.unboxed)
364+
self.init(key: key, box: sharedUnkeyedBox.unboxed, attributes: attributes)
363365
case let sharedKeyedBox as SharedBox<KeyedBox>:
364-
self.init(key: key, box: sharedKeyedBox.unboxed)
366+
self.init(key: key, box: sharedKeyedBox.unboxed, attributes: attributes)
365367
case let sharedChoiceBox as SharedBox<ChoiceBox>:
366-
self.init(key: key, box: sharedChoiceBox.unboxed)
368+
self.init(key: key, box: sharedChoiceBox.unboxed, attributes: attributes)
367369
case let unkeyedBox as UnkeyedBox:
368-
self.init(key: key, box: unkeyedBox)
370+
self.init(key: key, box: unkeyedBox, attributes: attributes)
369371
case let keyedBox as KeyedBox:
370-
self.init(key: key, box: keyedBox)
372+
self.init(key: key, box: keyedBox, attributes: attributes)
371373
case let choiceBox as ChoiceBox:
372-
self.init(key: key, box: choiceBox)
374+
self.init(key: key, box: choiceBox, attributes: attributes)
373375
case let simpleBox as SimpleBox:
374-
self.init(key: key, box: simpleBox)
376+
self.init(key: key, box: simpleBox, attributes: attributes)
375377
case let box:
376378
preconditionFailure("Unclassified box: \(type(of: box))")
377379
}

Sources/XMLCoder/Encoder/XMLEncoder.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,28 +318,30 @@ open class XMLEncoder {
318318
///
319319
/// - parameter value: The value to encode.
320320
/// - parameter withRootKey: the key used to wrap the encoded values.
321+
/// - parameter rootAttributes: the list of attributes to be added to root node
321322
/// - returns: A new `Data` value containing the encoded XML data.
322323
/// - throws: `EncodingError.invalidValue` if a non-conforming
323324
/// floating-point value is encountered during encoding, and the encoding
324325
/// strategy is `.throw`.
325326
/// - throws: An error if any value throws an error during encoding.
326-
open func encode<T: Encodable>(_ value: T, withRootKey rootKey: String, header: XMLHeader? = nil) throws -> Data {
327+
open func encode<T: Encodable>(_ value: T, withRootKey rootKey: String, rootAttributes: [String : String], header: XMLHeader? = nil) throws -> Data {
327328
let encoder = XMLEncoderImplementation(
328329
options: options,
329330
nodeEncodings: []
330331
)
331332
encoder.nodeEncodings.append(options.nodeEncodingStrategy.nodeEncodings(forType: T.self, with: encoder))
332333

333334
let topLevel = try encoder.box(value)
335+
let attributes = rootAttributes.map(Attribute.init)
334336

335337
let elementOrNone: XMLCoderElement?
336338

337339
if let keyedBox = topLevel as? KeyedBox {
338-
elementOrNone = XMLCoderElement(key: rootKey, box: keyedBox)
340+
elementOrNone = XMLCoderElement(key: rootKey, box: keyedBox, attributes: attributes)
339341
} else if let unkeyedBox = topLevel as? UnkeyedBox {
340-
elementOrNone = XMLCoderElement(key: rootKey, box: unkeyedBox)
342+
elementOrNone = XMLCoderElement(key: rootKey, box: unkeyedBox, attributes: attributes)
341343
} else if let choiceBox = topLevel as? ChoiceBox {
342-
elementOrNone = XMLCoderElement(key: rootKey, box: choiceBox)
344+
elementOrNone = XMLCoderElement(key: rootKey, box: choiceBox, attributes: attributes)
343345
} else {
344346
fatalError("Unrecognized top-level element of type: \(type(of: topLevel))")
345347
}

0 commit comments

Comments
 (0)