Skip to content

Commit cbdf66c

Browse files
committed
Resolve #5 Allow for CharData to be keyed
- In the presence of attributes and child elements, chardata values will be keyed on a custom value provided by the user via the new property `characterDataToken ` on the XMLDecoder
1 parent ebfc6c8 commit cbdf66c

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

XMLParsing/XML/Decoder/XMLDecoder.swift

100644100755
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ open class XMLDecoder {
114114
/// Contextual user-provided information for use during decoding.
115115
open var userInfo: [CodingUserInfoKey : Any] = [:]
116116

117+
118+
/// When an XML Element has both attributes and child elements, the CharData within the element will be keyed with the `characterDataToken`
119+
/// - Note The following XML has both attribute data, child elements, and character data:
120+
/// ```
121+
/// <SomeElement SomeAttribute="value">
122+
/// some string value
123+
/// <SomeOtherElement>valuevalue</SomeOtherElement>
124+
/// </SomeElement>
125+
/// ```
126+
/// The "some string value" will be keyed on "CharData"
127+
open var characterDataToken: String?
128+
117129
/// Options set on the top-level encoder to pass down the decoding hierarchy.
118130
internal struct _Options {
119131
let dateDecodingStrategy: DateDecodingStrategy
@@ -145,7 +157,7 @@ open class XMLDecoder {
145157
open func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T {
146158
let topLevel: [String: Any]
147159
do {
148-
topLevel = try _XMLStackParser.parse(with: data)
160+
topLevel = try _XMLStackParser.parse(with: data, charDataToken: self.characterDataToken)
149161
} catch {
150162
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid XML.", underlyingError: error))
151163
}

XMLParsing/XML/XMLStackParser.swift

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,17 @@ internal class _XMLElement {
157157
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
158158
}
159159

160-
func flatten() -> [String: Any] {
160+
func flatten(with charDataKey: String? = nil) -> [String: Any] {
161161
var node: [String: Any] = attributes
162162

163163
for childElement in children {
164164
for child in childElement.value {
165-
if let content = child.value {
166-
node[childElement.key] = content
167-
} else if !child.children.isEmpty || !child.attributes.isEmpty {
168-
let newValue = child.flatten()
165+
if !child.children.isEmpty || !child.attributes.isEmpty {
166+
var newValue = child.flatten(with: charDataKey)
167+
if let content = child.value,
168+
let charDataKey = charDataKey {
169+
newValue[charDataKey] = content
170+
}
169171

170172
if let existingValue = node[childElement.key] {
171173
if var array = existingValue as? Array<Any> {
@@ -177,6 +179,8 @@ internal class _XMLElement {
177179
} else {
178180
node[childElement.key] = newValue
179181
}
182+
} else if let content = child.value {
183+
node[childElement.key] = content
180184
}
181185
}
182186
}
@@ -248,12 +252,30 @@ internal class _XMLStackParser: NSObject, XMLParserDelegate {
248252
var currentElementName: String?
249253
var currentElementData = ""
250254

251-
static func parse(with data: Data) throws -> [String: Any] {
255+
256+
/// Parses the XML data and returns a dictionary of the parsed output
257+
///
258+
/// - Parameters:
259+
/// - data: The Data to be parsed
260+
/// - charDataToken: The token to key the charData in mixed content elements off of
261+
/// - Returns: Dictionary of the parsed XML
262+
/// - Throws: DecodingError if there's an issue parsing the XML
263+
/// - Note:
264+
/// When an XML Element has both attributes and child elements, the CharData within the element will be keyed with the `characterDataToken`
265+
/// The following XML has both attribute data, child elements, and character data:
266+
/// ```
267+
/// <SomeElement SomeAttribute="value">
268+
/// some string value
269+
/// <SomeOtherElement>valuevalue</SomeOtherElement>
270+
/// </SomeElement>
271+
/// ```
272+
/// The "some string value" will be keyed on "CharData"
273+
static func parse(with data: Data, charDataToken: String? = nil) throws -> [String: Any] {
252274
let parser = _XMLStackParser()
253275

254276
do {
255277
if let node = try parser.parse(with: data) {
256-
return node.flatten()
278+
return node.flatten(with: charDataToken)
257279
} else {
258280
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data could not be parsed into XML."))
259281
}

0 commit comments

Comments
 (0)