asantiagot
asantiagot

Reputation: 153

Parsing XML in Swift with NSXMLParser

I have the following XML structure which I want to parse:

<plist version="1.0">
        <dict>
            <key>
                PALABRA
            </key>
            <array>
                <string>
                    CATEGORY
                </string>
                <string>
                    WORD
                </string>
            </array>
        </dict>
</plist> 

I am using NSXMLParser in this way to parse the XML:

var posts = NSMutableArray()
var parser = NSXMLParser()
var elements = NSMutableDictionary()
var element = NSString()
var CATEGORY = NSMutableString()
var WORD = NSMutableString()

func parseXML() {
        posts = []
        parser = NSXMLParser(contentsOfURL:(NSURL(string:"http://www.serverbpw.com/cm/2016-1/hangman.php"))!)!
        parser.delegate = self
        parser.parse()         
}

func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {

    element = elementName
    if (elementName as NSString).isEqualToString("array") {
        elements = NSMutableDictionary()
        elements = [:]
        categoria = NSMutableString()
        categoria = ""
    }

    if (elementName as NSString).isEqualToString("string") {
        palabra = NSMutableString()
        palabra = ""
    }
}

func parser(parser: NSXMLParser, foundCharacters string: String) {

    if element.isEqualToString("string") {
        categoria.appendString(string)
    }

    if element.isEqualToString("string") {
        palabra.appendString(string)
    }
}

func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    if (elementName as NSString).isEqualToString("array") {
        if !categoria.isEqual(nil) {
            elements.setObject(categoria, forKey: "CATEGORIA")
        }
        if !palabra.isEqual(nil) {
            elements.setObject(palabra, forKey: "PALABRA")
        }            
        posts.addObject(elements)
    }
}

The value I get from the server for CATEGORY is the content of CATEGORY, but also the content of WORD, and the value for WORD is only WORD content. I know this is not the right way to parse the XML. The problem is that I don´t know how to get the CATEGORY and WORD elements, since both of them have the same ID ("String"). What would be the right way to get this information?

The server address is: http://www.serverbpw.com/cm/2016-1/hangman.php

Thanks in advance!

Upvotes: 0

Views: 2240

Answers (2)

Rob
Rob

Reputation: 438297

If you're going to parse this yourself, the trick is that you have two occurrences of <string>, and you have to differentiate them somehow. You could have your own counter to keep track of which one was which:

var posts: [[String: String]]!
var element: String!
var categoria: String?
var palabra: String?
var stringValue: String?
var counter: Int!

func parseXML(url: NSURL) {
    posts = [[:]]
    let parser = NSXMLParser(contentsOfURL: url)!
    parser.delegate = self
    parser.parse()
}

func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
    element = elementName
    if elementName == "array" {
        categoria = nil
        palabra = nil
        counter = 0
    }
}

func parser(parser: NSXMLParser, foundCharacters string: String) {
    if element == "string" {
        if stringValue == nil {
            stringValue = string
        } else {
            stringValue! += string
        }
    }
}

func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    if elementName == "string" {
        stringValue = stringValue?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
        if counter == 0 {
            categoria = stringValue
        } else if counter == 1 {
            palabra = stringValue
        }
        stringValue = nil
        counter!++
    }

    if elementName == "array" {
        var element = [String: String]()
        if categoria != nil {
            element["CATEGORIA"] = categoria
        }
        if palabra != nil {
            element["PALABRA"] = palabra
        }            
        posts.append(element)
    }
}

But I agree with cezheng that this appears to by a NSDictionary plist, and as such, you should just use NSDictionary(contentsOfFile) or what have you. And if this is really a XML that you're building, I'd suggest a different format, e.g.:

<posts>
    <post>
        <category>
            CATEGORY
        </category>
        <word>
            WORD
        </word>
    </post>
</posts> 

Then you could parse this far more easily, without the silliness of the counter.

Upvotes: 2

cezheng
cezheng

Reputation: 564

This is a plist so you can just use NSDictionary(contentsOfFile:).

if let dict = NSDictionary(contentsOfFile: filePath) {
  let array = dict["PALABRA"] as? [String]
  let category = array?[0]
  let word = array?[1]
}

if you don't have the data as a file, use this method

if let dict = try NSPropertyListSerialization.propertyListWithData(xmlData, options: .Immutable, format: nil) as? NSDictionary {
    //......
}

Upvotes: 1

Related Questions