Accessing element values ​​when using NSXMLParser in Swift

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

Using the following code on 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 name is \(elementName)")
    println("Element attributes are \(attributeDict)")
    }
}

var instance = Parse()

instance.beginParse()

      

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

Why does my dict attribute appear to be empty and how can I access the values ​​associated with these elements?

+3


source to share


3 answers


Parsing an XML file is NSXMLParser

not as easy as you might expect. You must implement several methods from the protocol NSXMLParserDelegate

to capture events. You have already done this for didStartElement

, which gives the expected results (for me): Element name and its attributes (there are no attributes in the XML file you linked above).

Everything works fine ... so far.

Now that you've caught the start of an element, you need to take care of further processing:



  • The protocol method foundCharacters

    runs between the start and end of the element. Maybe several times. You have to implement this method and add the characters found to the string variable depending on the element name.

  • When didEndElement

    run, your string variable is fully populated with the content of the element

If the XML file is deeply hierarchical, this can get complicated.

Sorry for this broad answer, but unfortunately this XML file is not there and give me a deeply nested word method with NSXMLParser

.

+5


source


Attributes are those included in the tag. For example, if you have a tag that looks like this:

<building id="foo">

      

In this case, it id

will be in attributeDict

.

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



<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, given <name>Commonwealth School</name>

this will result in a series of calls

  • didStartElement

    ,
  • foundCharacters

    (which can be called multiple times) and
  • didEndElement

    ...
+2


source


I made a simple example app that reads XML from this feed - jokes-n-fun RSS feed and displays the extracted data in the view table, here is the link: jokes-n-fun @github , generic source can be used as a link to create similar iOS applications. To answer your question, you can refer to the code written in the AtomParser.swift class , copy the same into it:

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)
    }
}

      

0


source







All Articles