Swift XML attribute parsing
A couple of observations:
Your
didStartElement
should simply look atattributeDict
to get the attributes for an element.You don't need a
foundCharacters
method in this case, because you're only parsing elements tags, and not parsing anything between the open and close tags.The
foundCharacters
method, if you even needed it, shouldn't have a privatedidStartElement
implemented within it.The only trick in your example is that you have very confusing XML with nested
day1
tags. I'd really suggest changing the XML to something that makes sense.But, if you're stuck with this, one fairly flexible solution is to keep a stack of element names (implemented as simple array,
elementNames
), push anelementName
onto the stack indidStartElement
(by adding to the end of the array ofelementNames
) and popping one off indidEndElement
(by callingremoveLast
). So, for example, by the time you hit the innerday1
tag inside your XML, the array ofelementNames
will be["root", "day1", "day1"]
.Now that you have that, you can check to see if you are looking at
day1
inside anotherday1
by seeing if the last two items inelementNames
are bothday1
.
For example:
func beginParsing(URL: NSURL) {
let parser = NSXMLParser(contentsOfURL: URL)!
parser.delegate = self
parser.parse()
print(schedule)
}
var schedule: [String: String]?
var elementNames = [String]()
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
elementNames.append(elementName)
let count = elementNames.count
if count >= 2 && elementNames[count - 2] == "day1" && elementNames[count - 1] == "day1" {
schedule = attributeDict
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
elementNames.removeLast()
}
func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
print(parseError)
}
How can I get a specific attribute from a XML in SWIFT with NSXMLParser
I figured it out by myself. Here is what I did in order to retrieve the value in case there's someone who encounter the same problem as me.
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if elementName == "reading"{
if attributeDict["type"] == "NPSI_PM25_3HR"{
let PSIValue = attributeDict["value"]! as String
print(i)
switch i {
case 0:
area = "NRS"
nationalPSI = PSIValue
case 1:
area = "North"
case 2:
area = "South"
case 3:
area = "Central"
case 4:
area = "West"
case 5:
area = "East"
default:
area = ""
}
i += 1
print(area, ":", PSIValue)
}
}
}
Swift XMLParser // Select node based on element & attribute
When I've needed to deal with this I've used a few instance variables to keep track of where I am.
var inInputs = false
var inOverlays = false
Then update your didStartElement
to look for "inputs" and set inInputs = true
. And look for overlays
and set inOverlays = true
. Do the same for didEndElement
but set the flags back to false
.
Then you can update your if elementName == "overlay" {
to if elementName == "overlay" && inOverlays {
. Make a similar change for input
.
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if elementName == "overlays" {
inOverlays = true
} else if elementName == "inputs" {
inInputs = true
} else if elementName == "overlay" && inOverlays {
OverlayValue = ""
}
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "overlays" {
inOverlays = false
} else if elementName == "inputs" {
inInputs = false
} else if elementName == "overlay" && inOverlays {
OverlayXML.append(OverlayArray(value: OverlayValue!))
OverlayValue = nil
}
}
Check if XML Element Exists in Swift
Store the names of all elements inside didStartElement
in an array until this method called
optional func parserDidEndDocument(_ parser: XMLParser)
then check the array content for that element name
Parse XML properties in Swift
You should compare the element name in parser:didStartElement:qualifiedName:attributes: and extract attributes from it to get url. It should be something like this. The attributes dict contains all the attributes that are in that element.
For example look at this particular element,
<media:thumbnail height="540" width="960" url="https://i.vimeocdn.com/video/611645334_960.webp"/>
If you read attributes dict for this, it should be like this,
["height": "540", "url": "https://i.vimeocdn.com/video/611645334_960.webp", "width": "960"]
Here is code for extracting url that you want,
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if elementName == "media:thumbnail" {
if let url = attributeDict["url"] {
print(url)
}
}
}
Swift parsing attribute name for given elementName
Building a dictionary of [city:id] can be a solution for you.
I have implemented a simple solution based on the article about lifecycle of NSXMLParser at http://www.codeproject.com/Articles/248883/Objective-C-Fundamentals-NSXMLParser .
Following method is called when when an element is starting.
You can retrieve city id attribute and save it in an instance level variable so that you can use it in the next method.
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject])
And then, Following method is called when the parser see anything between starting and ending.
func parser(parser: NSXMLParser!, foundCharacters string: String!)
So, you can get the city name from here.
Now we have city id and city name to add a new item into the [city:id] dictionary.
Once you build the dictionary, searching would be very simple.
here is my working test code.
class ViewController: UIViewController ,NSXMLParserDelegate{
var parser: NSXMLParser!
var city: String = String()
var ifDirOK = false
var ifCityNameOK = false
var element : String?
var value: String=String()
var dic = Dictionary<String,String>()
var currentCityId:String?
@IBOutlet weak var result: UILabel!
@IBOutlet weak var search: UITextField! //search text
@IBAction func ActionGoGetIt(sender: AnyObject) {
self.result.text=dic[self.search.text]
}
override func viewDidLoad() {
super.viewDidLoad()
let url: NSURL = NSURL(string: "https://pogoda.yandex.ru/static/cities.xml")!
parser = NSXMLParser(contentsOfURL: url)
parser.delegate = self
parser.parse()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) {
element = elementName
if (element == "city"){
ifDirOK = true
let cityID = attributeDict ["id"] as? NSString
self.currentCityId = cityID as? String
}
}
func parser(parser: NSXMLParser!, foundCharacters string: String!) {
var data = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
if (!data.isEmpty){
if (element == "city"){
dic[data] = self.currentCityId as String?
}
}
}
func parser(parser: NSXMLParser, foundAttributeDeclarationWithName attributeName: String, forElement elementName: String, type: String?, defaultValue: String?) {
if (ifDirOK && ifCityNameOK){
println("\(attributeName)")
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
}
}
Related Topics
Testing If a Decimal Is a Whole Number in Swift
Create Complicated Nscompoundpredicate in Swift 3
Limit Textfield to X Amount of Characters Using Swiftui
Split Paragraphs into Sentences
Swift: How to Fix Infinite Loop When Adding a Value to a Firebase Variable
Moving Keyboard When Editing Multiple Textfields with Constraints Swift
Swift Generics Error: Cannot Convert Value of Type 'Type<T>' to Expected Argument Type 'Type<_>'
How to Pass/Get Core Data Context in Swiftui Mvvm Viewmodel
Access Flutter Sharedpreferences in Swift
Swift Generics: Non-Nominal Type Does Not Support Explicit Initialization
Xcode 9 Fails to Build Swift 4 Project with Pod
Can You Enforce a Typealias in Swift
Tapping an Mkmapview in Swiftui
Get Compiler Error in Swift Indexof()
Inline Kvo of a Property in Another View Controller
Shows the Alert When Uitextfield's Are Full or Empty Swift