diff --git a/XMLParsing/XML/Decoder/XMLDecoder.swift b/XMLParsing/XML/Decoder/XMLDecoder.swift
old mode 100644
new mode 100755
index 78ee8e9..ea38518
--- a/XMLParsing/XML/Decoder/XMLDecoder.swift
+++ b/XMLParsing/XML/Decoder/XMLDecoder.swift
@@ -114,6 +114,18 @@ open class XMLDecoder {
/// Contextual user-provided information for use during decoding.
open var userInfo: [CodingUserInfoKey : Any] = [:]
+
+ /// When an XML Element has both attributes and child elements, the CharData within the element will be keyed with the `characterDataToken`
+ /// - Note The following XML has both attribute data, child elements, and character data:
+ /// ```
+ ///
+ /// some string value
+ /// valuevalue
+ ///
+ /// ```
+ /// The "some string value" will be keyed on "CharData"
+ open var characterDataToken: String?
+
/// Options set on the top-level encoder to pass down the decoding hierarchy.
internal struct _Options {
let dateDecodingStrategy: DateDecodingStrategy
@@ -145,7 +157,7 @@ open class XMLDecoder {
open func decode(_ type: T.Type, from data: Data) throws -> T {
let topLevel: [String: Any]
do {
- topLevel = try _XMLStackParser.parse(with: data)
+ topLevel = try _XMLStackParser.parse(with: data, charDataToken: self.characterDataToken)
} catch {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid XML.", underlyingError: error))
}
diff --git a/XMLParsing/XML/XMLStackParser.swift b/XMLParsing/XML/XMLStackParser.swift
index 6aea4f6..d9e1625 100644
--- a/XMLParsing/XML/XMLStackParser.swift
+++ b/XMLParsing/XML/XMLStackParser.swift
@@ -157,15 +157,17 @@ internal class _XMLElement {
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
}
- func flatten() -> [String: Any] {
+ func flatten(with charDataKey: String? = nil) -> [String: Any] {
var node: [String: Any] = attributes
for childElement in children {
for child in childElement.value {
- if let content = child.value {
- node[childElement.key] = content
- } else if !child.children.isEmpty || !child.attributes.isEmpty {
- let newValue = child.flatten()
+ if !child.children.isEmpty || !child.attributes.isEmpty {
+ var newValue = child.flatten(with: charDataKey)
+ if let content = child.value,
+ let charDataKey = charDataKey {
+ newValue[charDataKey] = content
+ }
if let existingValue = node[childElement.key] {
if var array = existingValue as? Array {
@@ -177,6 +179,8 @@ internal class _XMLElement {
} else {
node[childElement.key] = newValue
}
+ } else if let content = child.value {
+ node[childElement.key] = content
}
}
}
@@ -248,12 +252,30 @@ internal class _XMLStackParser: NSObject, XMLParserDelegate {
var currentElementName: String?
var currentElementData = ""
- static func parse(with data: Data) throws -> [String: Any] {
+
+ /// Parses the XML data and returns a dictionary of the parsed output
+ ///
+ /// - Parameters:
+ /// - data: The Data to be parsed
+ /// - charDataToken: The token to key the charData in mixed content elements off of
+ /// - Returns: Dictionary of the parsed XML
+ /// - Throws: DecodingError if there's an issue parsing the XML
+ /// - Note:
+ /// When an XML Element has both attributes and child elements, the CharData within the element will be keyed with the `characterDataToken`
+ /// The following XML has both attribute data, child elements, and character data:
+ /// ```
+ ///
+ /// some string value
+ /// valuevalue
+ ///
+ /// ```
+ /// The "some string value" will be keyed on "CharData"
+ static func parse(with data: Data, charDataToken: String? = nil) throws -> [String: Any] {
let parser = _XMLStackParser()
do {
if let node = try parser.parse(with: data) {
- return node.flatten()
+ return node.flatten(with: charDataToken)
} else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data could not be parsed into XML."))
}