I'm racking my brain how to convert this parsed xml into arrays or dictionaries. the xml tags are not helpful because the labels are generic and there are ~10 headers. I might be able to do something based on the order of the labels. any ideas?
NSXMLParser Method Code:
class MyXMLParserDelegate: NSObject, NSXMLParserDelegate {
@objc func parserDidStartDocument(parser: NSXMLParser) {
print("parserDidStartDocument")
}
@objc func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
print("didStartElement --> \(elementName)")
}
@objc func parser(parser: NSXMLParser, foundCharacters string: String) {
print("foundCharacters --> \(string)")
}
@objc func parser(parser: NSXMLParser, didEndElement elementName: String,
namespaceURI: String?, qualifiedName qName: String?) {
print("didEndElement --> \(elementName)")
}
@objc func parser(parser: NSXMLParser, didStartMappingPrefix prefix: String,
toURI namespaceURI: String) {
print("didStartMappingPrefix --> Prefix: \(prefix) toURI: \(namespaceURI)")
}
@objc func parser(parser: NSXMLParser, didEndMappingPrefix prefix: String) {
print("didEndMappingPrefix --> Prefix: \(prefix)")
}
@objc func parserDidEndDocument(parser: NSXMLParser) {
//reload table with array
print("parserDidEndDocument")
}
}
Sample results of XML parsing using NSXMLParser methods:
<result>
<header>
<col>
<label>Tree Name</label>
</col>
<col>
<label>Num Levels</label>
</col>
<col>
<label>Defaults Weight</label>
</col>
<col>
<label>Name</label>
</col>
<col>
<label>Abbrev</label>
</col>
<col>
<label>Level</label>
</col>
<col>
<label>Full Name</label>
</col>
</header>
<body>
<row>
<col>Cost Center 1</col>
<col>2</col>
<col>5</col>
<col>Miami Dolphins Front Office</col>
<col/>
<col>0</col>
<col/>
</row>
<row>
<col>Cost Center 1</col>
<col>2</col>
<col>5</col>
<col>Accounts Receivable</col>
<col>A/R</col>
<col>1</col>
<col>Accounts Receivable</col>
</row>
<row>
<col>Cost Center 1</col>
<col>2</col>
<col>5</col>
<col>06</col>
<col>06</col>
<col>1</col>
<col>06</col>
</row>
<row>
<col>Cost Center 2</col>
<col>3</col>
<col>5</col>
<col>Cost Center 2</col>
<col/>
<col>0</col>
<col/>
</row>
<row>
<col>Cost Center 2</col>
<col>3</col>
<col>5</col>
<col>test2</col>
<col/>
<col>1</col>
<col>test2</col>
</row>
<row>
<col>Cost Center 2</col>
<col>3</col>
<col>5</col>
<col>test</col>
<col/>
<col>1</col>
<col>test</col>
</row>
<row>
<col>Cost Center 3</col>
<col>3</col>
<col>5</col>
<col>Cost Center 3</col>
<col/>
<col>0</col>
<col/>
</row>
<row>
<col>Cost Center 3</col>
<col>3</col>
<col>5</col>
<col>test</col>
<col/>
<col>1</col>
<col>test</col>
</row>
</body>
<footer/>
</result>
parserDidStartDocument
didStartElement --> result
foundCharacters -->
didStartElement --> header
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Tree Name
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Num Levels
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Defaults Weight
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Name
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Abbrev
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Level
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters -->
didStartElement --> label
foundCharacters --> Full Name
didEndElement --> label
foundCharacters -->
didEndElement --> col
foundCharacters -->
didEndElement --> header
foundCharacters -->
didStartElement --> body
foundCharacters -->
didStartElement --> row
foundCharacters -->
didStartElement --> col
foundCharacters --> Cost Center 1
didEndElement --> col
foundCharacters -->
didStartElement --> col
foundCharacters --> 2
didEndElement --> col
foundCharacters -->
...
I needed something like this for testing generated XML but had to roll my own. I created a nested tree of elements, each containing information about the xml tag.
fileprivate class XmlToDictionaryParserDelegate: NSObject, XMLParserDelegate {
private var currentElement: XmlElement?
fileprivate init(_ element: XmlElement) {
self.currentElement = element
}
public func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
self.currentElement = self.currentElement?.pop(elementName)
}
public func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
self.currentElement = self.currentElement?.push(elementName)
self.currentElement?.attributeDict = attributeDict
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
self.currentElement?.text += string
}
}
public class XmlElement {
public private(set) var name = "unnamed"
public private(set) var children = [String: XmlElement]()
public private(set) var parent: XmlElement? = nil
public fileprivate(set) var text = ""
public fileprivate(set) var attributeDict: [String : String] = [:]
private init(_ parent: XmlElement? = nil, name: String = "") {
self.parent = parent
self.name = name
}
public convenience init?(fromString: String) {
guard let data = fromString.data(using: .utf8) else {
return nil
}
self.init(fromData: data)
}
public init(fromData: Data) {
let parser = XMLParser(data: fromData)
let delegate = XmlToDictionaryParserDelegate(self)
parser.delegate = delegate
parser.parse()
}
fileprivate func push(_ elementName: String) -> XmlElement {
let childElement = XmlElement(self, name: elementName)
children[elementName] = childElement
return childElement
}
fileprivate func pop(_ elementName: String) -> XmlElement? {
assert(elementName == self.name)
return self.parent
}
public subscript(name: String) -> XmlElement? {
return self.children[name]
}
}
To use create an element from a string (or data)
let xml = XmlElement(fromString: "<first>text<second bar="foo"/></first>")
Then use like this:
XCTAssert(xml["first"]?.text == "text")
XCTAssert(xml["first"]?["second"].attributeDict["bar"] == "foo")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With