Skip to content

Commit

Permalink
Resolve ShawnMoore#5 Allow for CharData to be keyed
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ciauri committed Mar 15, 2018
1 parent ebfc6c8 commit cbdf66c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 8 deletions.
14 changes: 13 additions & 1 deletion XMLParsing/XML/Decoder/XMLDecoder.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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:
/// ```
/// <SomeElement SomeAttribute="value">
/// some string value
/// <SomeOtherElement>valuevalue</SomeOtherElement>
/// </SomeElement>
/// ```
/// 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
Expand Down Expand Up @@ -145,7 +157,7 @@ open class XMLDecoder {
open func decode<T : Decodable>(_ 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))
}
Expand Down
36 changes: 29 additions & 7 deletions XMLParsing/XML/XMLStackParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any> {
Expand All @@ -177,6 +179,8 @@ internal class _XMLElement {
} else {
node[childElement.key] = newValue
}
} else if let content = child.value {
node[childElement.key] = content
}
}
}
Expand Down Expand Up @@ -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:
/// ```
/// <SomeElement SomeAttribute="value">
/// some string value
/// <SomeOtherElement>valuevalue</SomeOtherElement>
/// </SomeElement>
/// ```
/// 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."))
}
Expand Down

0 comments on commit cbdf66c

Please sign in to comment.