Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse XML data in swift

Tags:

xml

swift

It's my first iOS application, and I kinda have some trouble with getting data from a XML. I need to get the song name and the artist from a XML file that looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Schedule System="Jazler">
  <Event status="happening" startTime="19:14:30" eventType="song">
    <Announcement Display="Now On Air:"/>
    <Song title="E timpul">
      <Artist name="Revers">
      </Artist>
      <Jazler ID="16490"/>
      <PlayLister ID=""/>
      <Media runTime="03:03"/>
      <Expire Time="19:17:33"/>
    </Song>
  </Event>
</Schedule>

Until now I think I created the parser, but I have no idea how to get the data from it, and the online tutorials are confusing me a bit...

self.parser = XMLParser(contentsOf: URL(string:"http://localhost/jazler/NowOnAir.xml")!)!
self.parser.delegate = self as? XMLParserDelegate

let success:Bool = self.parser.parse()
if success {
    print("success")

} else {
    print("parse failure!")
}

Your help is much appreciated & thanks in advance.

like image 345
Amos94 Avatar asked Feb 15 '18 17:02

Amos94


2 Answers

Because your XML contains all of the values with attributes of the element, you don't need to implement foundCharacters. Just didStartElement, e.g., your parser delegate might look as simple as:

var song: String?
var artist: String?

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
    switch elementName {
    case "Song":   song   = attributeDict["title"]
    case "Artist": artist = attributeDict["name"]
    default:       break
    }
}

Two observations:

  1. I'd be inclined to pull this parsing code out of the view controller, though, and put it in a dedicated object, to help prevent "view controller bloat".

  2. I'd also use URLSession in case the response to request happens to be a little slow. Generally, one should avoid using XMLParser(contentsOf:), because that performs the request synchronously.

    In your case, since you’re requesting the data from localhost, perhaps that’s less of a concern. But, still, it’s prudent to always perform HTTP requests asynchronously.

Anyway, that might yield something like:

class SongParser: NSObject {
    var song: String?
    var artist: String?
    
    class func requestSong(completionHandler: @escaping (String?, String?, Error?) -> Void) {
        let url = URL(string: "http://localhost/jazler/NowOnAir.xml")!
        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            guard let data = data, error == nil else {
                DispatchQueue.main.async {
                    completionHandler(nil, nil, error)
                }
                return
            }
            
            let delegate = SongParser()
            let parser = XMLParser(data: data)
            parser.delegate = delegate
            parser.parse()
            DispatchQueue.main.async {
                completionHandler(delegate.song, delegate.artist, parser.parserError)
            }
        }
        task.resume()
    }
}

extension SongParser: XMLParserDelegate {
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        switch elementName {
        case "Song":   song   = attributeDict["title"]
        case "Artist": artist = attributeDict["name"]
        default:       break
        }
    }
    
}

And you'd use it like so:

SongParser.requestSong { song, artist, error in
    guard let song = song, let artist = artist, error == nil else {
        print(error ?? "Unknown error")
        return
    }
    
    print("Song:", song)
    print("Artist:", artist)
}
like image 68
Rob Avatar answered Oct 23 '22 23:10

Rob


First convert your xml into NSData and call the parser to parse it.

//converting into NSData
var data: Data? = theXML.data(using: .utf8)

//initiate  NSXMLParser with this data
var parser: XMLParser? = XMLParser(data: data ?? Data())

//setting delegate
parser?.delegate = self

//call the method to parse
var result: Bool? = parser?.parse()

parser?.shouldResolveExternalEntities = true

Now, you need to implement the NSXMLParser delegate into your class.

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
currentElement = elementName
print("CurrentElementl: [\(elementName)]")
}

func parser(_ parser: XMLParser, foundCharacters string: String) {
print("foundCharacters: [\(string)]")
}

You will find the value under key of your xml.

like image 34
Md. Mostafizur Rahman Avatar answered Oct 23 '22 23:10

Md. Mostafizur Rahman