3

I am trying to parse the XML on http://apps.wku.edu/iwku/maps/buildings/data/SouthCampus-Buildings.xml

Using the following code in a Swift Playground

import UIKit

var str = "Hello, playground"

class Parse: NSObject, NSXMLParserDelegate{

func beginParse(){        
    let url = NSURL(string: "http://apps.wku.edu/iwku/maps/buildings/data/SouthCampus-Buildings.xml")
    var xml = NSXMLParser(contentsOfURL: url)

    xml?.delegate = self
    xml?.parse()
}

func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: NSDictionary!) {
    println("Element's name is \(elementName)")
    println("Element's attributes are \(attributeDict)")
    }
}

var instance = Parse()

instance.beginParse()

Unfortunately my output in the console looks like this: enter image description here

How come my attributeDict appears to be empty, and how can I access the values associated with these elements?

DosCadenas
  • 95
  • 1
  • 10

3 Answers3

5

Parsing an XML file with NSXMLParser is not that easy as you expect here. You have to implement several methods from the NSXMLParserDelegate protocol to catch events. You already did that for didStartElement which gives the (for me) expected results: The element name and its attributes (there are no attributes in the XML file you linked above).

Everything works fine... so far.

Now that you catched the start of an element you have to take care on the further processing:

  • The protocol method foundCharacters is fired between the start and the end of an element. Maybe several times. You have to implement that method and append the characters found to a string variable, depending on the name of the element.

  • When didEndElement is fired, your string variable is filled completely with the content of the element

If the XML file has a deep hierachically structure things may get complicated more and more.

Sorry for this broad answer but unfortunately there is no take that XML file and give me a deeply nested dictionary method with NSXMLParser.

zisoft
  • 22,770
  • 10
  • 62
  • 73
  • After browsing the internet I found the same thing. I was able to implement the two methods to correctly get the values. Thanks! – DosCadenas Dec 16 '14 at 19:11
2

The attributes are those which are included inside the tag. For example, if you have a tag that looked like:

<building id="foo">

In that case, id would be in the attributeDict.

In this case, though, you have XML that looks like:

<buildings>
    <building>
        <name>Commonwealth School</name>
        <building_code>SC</building_code>
        <latitude>36.965075</latitude>
        <longitude>-86.467144</longitude>
        <image_url>
        http://www.wku.edu/marketingandcommunications/images/wkucuptallrb.jpg
        </image_url>
        <description/>
        <handicap_accessible/>
        <address/>
        <url/>
        <aliases>
            <alias>South Campus</alias>
            <alias>Community College</alias>
        </aliases>
        <email/>
        <phone/>
        <organizations/>
    </building>
    ...

So, considering <name>Commonwealth School</name>, that will result in series of calls to

  • didStartElement,
  • foundCharacters (which may be called multiple times), and
  • didEndElement.
Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

I have made a simple sample app which reads XML from this feed - jokes-n-fun RSS feed and displays retrieved data in a table view, here is the link: jokes-n-fun@github, the shared source code can be used as a reference for making similar iOS apps. To answer your question you can refer code written in AtomParser.swift class, copy pasting the same over here:

import UIKit

// MARK: - declaring typealias to store closures with dictionary
typealias StartTagRuleBlock = (NSMutableDictionary, [NSObject : AnyObject]) -> Void
typealias EndTagRuleBlock = (NSMutableDictionary, String) -> Void

// MARK: - Protocol declared
protocol CompletionObserver {
    func dataSourcePopulated(dataSourceArray : NSArray) -> ()
}

class AtomParser : NSObject, NSXMLParserDelegate {
    // MARK: - Properties
    let triggerTag : String
    let parseUrl : String

    var dataArray : NSMutableArray = NSMutableArray()

    var collectedCharacters : NSMutableString?
    var recordDict : NSMutableDictionary?
    var parser : NSXMLParser?
    var startElementRuleMappingDict:[String: StartTagRuleBlock] = [String: StartTagRuleBlock]()
    var endElementRuleMappingDict:[String: EndTagRuleBlock] = [String: EndTagRuleBlock]()
    var endTagRules : NSDictionary?

    var completionObserver : CompletionObserver?

    // MARK: - Designated initializer
    /**
    Designated initializer to initialize AtomParser class.

    :param: triggerTag     Tag which distinguishes each record.
    :param: parseUrl       URL supplying xml to be parser.

    :returns: Void.
    */
    init(triggerTag : String, parseUrl : String) {
        self.triggerTag = triggerTag
        self.parseUrl = parseUrl
    }

    // MARK: - Initiate parsing
    func startParsing () {
        let url = NSURL(string: self.parseUrl)
        parser = NSXMLParser(contentsOfURL: url)
        parser?.delegate = self
        parser?.parse()
    }

    // MARK: - Parser delegates
    func parserDidStartDocument(parser: NSXMLParser!) {
        self.dataArray.removeAllObjects()
    }

    func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
        if elementName == triggerTag {
            recordDict = NSMutableDictionary(capacity: 1)
        }
        else if recordDict != nil {
            if let startTagMappingElement = self.startElementRuleMappingDict[elementName] {
                startTagMappingElement(recordDict!,attributeDict)
            }

            collectedCharacters = NSMutableString(string: "")

        }
        else {
            // no need to handle these tags
        }
    }

    func parser(parser: NSXMLParser!, foundCharacters string: String!) {
        collectedCharacters?.appendString(string)
    }

    func parser(parser: NSXMLParser!, didEndElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!) {
        if let mutableDictionary = recordDict {
            if elementName == triggerTag {
                dataArray.addObject(recordDict!)
            }
            else if recordDict != nil {
                if let endTagMappingElement = self.endElementRuleMappingDict[elementName] {
                    endTagMappingElement(recordDict!,"\(String(collectedCharacters!))")  
                }
            }
            else {
                // no need to handle these tags
            }
        }
    }

    func parserDidEndDocument(parser: NSXMLParser) {
        let arrayToReturn = NSArray(array: dataArray)
        completionObserver?.dataSourcePopulated(arrayToReturn)
    }
}
Devarshi
  • 16,440
  • 13
  • 72
  • 125