Though the XML file format sounds outdated in comparison with the JSON file format, XML could be very a lot alive. JSON could be extra in style in internet growth, however XML has many options that JSON lacks, equivalent to namespace assist that’s helpful to keep away from identify collision for components and attributes. XML additionally has a schema that may power an XML doc to stick to a sure customary.
You don’t have to search for a third-party library to control an XML file with Swift. The Swift customary library has XMLParser
for all of your XML wants.
Leap forward:
Organising XMLParser
and XMLParserDelegate
Create a Playground undertaking in XCode and identify it XMLParsingPlayground
. Check with this text to study extra about this step.
To parse the XML knowledge, it’s essential to convert an XML string to Knowledge
. Add the next code:
let xmlContent = """ <?xml model="1.0" encoding="UTF-8"?> <root> <article> <title>Getting Began with Swift</title> </article> <article> <title> Parse XML with Rust</title> </article> </root> """; let xmlData = Knowledge(xmlContent.utf8)
Within the code above, you initialized a Knowledge
occasion with an XML string. Now, it’s essential to initialize an XMLParser
occasion with this Knowledge
occasion:
let xmlParser = XMLParser(knowledge: xmlData)
XMLParser
will delegate the parsing logic to its delegation class. You’ll have to implement this class that inherits NSObject
and XMLParserDelegate
. Add the next code to create the category and its occasion:
class Parser1 : NSObject, XMLParserDelegate { func parserDidStartDocument(_ parser: XMLParser) { print("Begin of the doc") print("Line quantity: (parser.lineNumber)") } func parserDidEndDocument(_ parser: XMLParser) { print("Finish of the doc") print("Line quantity: (parser.lineNumber)") } } let parser1 = Parser1()
Within the delegated class, you arrange a logic to do one thing once you hit the beginning of the doc and the tip of the doc within the parsing course of. For those who encounter these, you’ll print some strings and a line quantity the place the parser is correct now. Each strategies settle for an XMLParser
occasion within the arguments. Many strategies within the delegated class settle for an XMLParser
occasion of their arguments.
When you get the parser delegation occasion executed, you need to set it because the XMLParser
occasion’s delegation class:
xmlParser.delegate = parser1
The final step is to parse it:
xmlParser.parse()
For those who compiled and ran this system, it is best to get the next output:
Begin of the doc Line quantity: 1 Finish of the doc Line quantity: 9
The code above implies that when the parser met the occasion of the begin of the doc
, it was in line 1. Then it went to the content material of the doc. It traveled by way of the weather however since you didn’t implement strategies to deal with this, nothing occurred. Lastly, when it hit the occasion of the finish of the doc
, it knowledgeable us by way of the parserDidEndDocument
methodology that you just’ve carried out.
Dealing with components of the XML knowledge
When parsing XML, it’s essential to implement strategies to deal with the nodes or occasions that you just’re fascinated about. For those who’re fascinated about feedback, it’s essential to implement a way when the parser meets feedback within the XML knowledge.
Now, you wish to implement strategies for when a parser meets components like article
and title
. The tactic that it’s essential to implement is the parser
methodology with the next signature:
func parser( _ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:] )
Create a brand new parser delegated class:
class Parser2 : NSObject, XMLParserDelegate { func parser( _ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:] ) { print(elementName) } }
Within the new delegated class, you didn’t implement parserDidStartDocument
and parserDidEndDocument
since you’re solely centered on parsing components.
As regular, it’s essential to set the delegated class occasion of a brand new XMLParser
occasion and name the parse
methodology once more:
let parser2 = Parser2() let xmlParser2 = XMLParser(knowledge: xmlData) xmlParser2.delegate = parser2 xmlParser2.parse()
Compile and run this system, and it’ll print the weather’ names:
root article title article title
As you possibly can see, there are 5 components. If you wish to differentiate title
components as a result of there are two of them, it’s essential to add extra logic. One of many examples is to trace what number of article
components the parse has met.
Create one other delegated class:
class Parser3 : NSObject, XMLParserDelegate { var articleNth = 0 func parser( _ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:] ) { if (elementName=="article") { articleNth += 1 } else if (elementName=="title") { print("'(elementName)' within the article component quantity (articleNth)") } } }
This time, you tracked what number of article
components you’ve met so once you meet the title
component, you already know which article
component you’re in.
Create a brand new XMLParser
occasion and set the delegated class to Parser3
:
let parser3 = Parser3() let xmlParser3 = XMLParser(knowledge: xmlData) xmlParser3.delegate = parser3 xmlParser3.parse()
For those who compile and run this system, it is best to see this output:
'title' within the article component number one 'title' within the article component quantity 2
Dealing with the attributes of components of the XML knowledge
Components within the XML knowledge can have attributes as properly. You would possibly wish to question the attributes. The latest methodology that you just’ve executed has one other argument that refers back to the attributes. However first, you need to create different XML knowledge as a result of the info proper now doesn’t have the attributes:
let xmlContent_attributes = """ <?xml model="1.0" encoding="UTF-8"?> <root> <article id="1" tag="swift"> <title>Getting Began with Swift</title> </article> <article id="2" tag="rust"> <title> Parse XML with Rust</title> </article> </root> """; let xmlData_attributes = Knowledge(xmlContent_attributes.utf8)
This time, you added attributes on the article
components. The attributes are id
and tag
.
Now, you need to create the delegated class to deal with the attributes:
class Parser4 : NSObject, XMLParserDelegate { func parser( _ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:] ) { for (attr_key, attr_val) in attributeDict { print("Key: (attr_key), worth: (attr_val)") } } }
The tactic is identical as the strategy you beforehand carried out. However this time, you didn’t ignore the attributeDict
argument.
We’re not completed but! Now, you need to create a brand new XMLParser
occasion that makes use of this delegated class occasion:
let parser4 = Parser4() let xmlParser4 = XMLParser(knowledge: xmlData_attributes) xmlParser4.delegate = parser4 xmlParser4.parse()
For those who compiled and ran this system, you’d see the attributes:
Key: id, worth: 1 Key: tag, worth: swift Key: id, worth: 2 Key: tag, worth: rust
Dealing with the namespace within the XML knowledge
Now, there are two arguments you haven’t handled within the parser
methodology. They’re namespaceURI
and qName
, that are associated to one another.
First, it’s essential to add namespaces into the XML knowledge:
let xmlContent_namespace = """ <?xml model="1.0" encoding="UTF-8"?> <root xmlns:t="http://logrocket.com/tech" xmlns:m="http://logrocket.com/advertising"> <t:article> <t:title>Getting Began with Swift</t:title> </t:article> <m:article> <m:title> Parse XML with Rust</m:title> </m:article> </root> """; let xmlData_namespace = Knowledge(xmlContent_namespace.utf8)
There are two namespaces on this XML knowledge: http://logrocket.com/tech
and http://logrocket.com/advertising
. Components inside the foundation component use namespaces. It’s not article
anymore, however t:article
or m:article
.
The t
refers to xmlns:t
, which has the worth http://logrocket.com/tech
. The m
refers to xmlns:m
, which has the worth http://logrocket.com/advertising
.
Then it’s essential to create the delegated class to deal with the namespace:
class Parser5 : NSObject, XMLParserDelegate { func parser( _ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:] ) { print("Namespace URI: (namespaceURI!), certified identify: (qName!)") } }
To get the namespace URI, for instance, http://logrocket.com/tech
, you should utilize the namespaceURI
argument. To get the certified identify, for instance, t:article
, you should utilize the qName
argument.
The following step is vital. Create a brand new XMLParser
occasion to deal with the namespace:
let parser5 = Parser5() let xmlParser5 = XMLParser(knowledge: xmlData_namespace) xmlParser5.delegate = parser5 xmlParser5.shouldProcessNamespaces = true xmlParser5.parse()
Earlier than you compile and run this system, discover there’s a line that tells the parser to course of the namespace. To allow the namespace within the XML knowledge, it’s essential to set the shouldProcessNamespaces
property of the parser to true
. In any other case, the namespaces shall be ignored.
Compile and run this system. Then you’ll get the namespace URIs and the certified names:
Namespace URI: , certified identify: root Namespace URI: http://logrocket.com/tech, certified identify: t:article Namespace URI: http://logrocket.com/tech, certified identify: t:title Namespace URI: http://logrocket.com/advertising, certified identify: m:article Namespace URI: http://logrocket.com/advertising, certified identify: m:title
Getting the textual content content material when parsing the XML knowledge
Now, it’s time to get the textual content content material from the XML knowledge. The tactic that it’s essential to implement is the parser
methodology with the next signature:
func parser( _ parser: XMLParser, foundCharacters string: String )
Add the next XML knowledge:
let xmlContent_text = """ <?xml model="1.0" encoding="UTF-8"?> <root> <article> <title>Getting Began with Swift</title> <revealed>true</revealed> </article> <article> <title> Parse XML with Rust</title> <revealed>false</revealed> </article> </root> """; let xmlData_text = Knowledge(xmlContent_text.utf8)
Then it’s essential to implement the delegated class that has a way to deal with the textual content content material:
class Parser6 : NSObject, XMLParserDelegate { func parser( _ parser: XMLParser, foundCharacters string: String ) { if (string.trimmingCharacters(in: .whitespacesAndNewlines) != "") { print(string) } } }
You’re solely within the textual content content material that has some characters. For those who didn’t implement the filter, it will end in plenty of clean textual content content material. The textual content context is outlined because the textual content between component nodes. That features the clean textual content between the article
component and the title
component as properly:
<article> <title> Parse XML with Rust</title>
It’s not simply white areas, however there’s a new line between article
and title
. That’s why you trimmed it first after which checked whether or not it was an empty string or not.
Lastly, create an XMLParser
occasion to deal with the textual content content material:
let parser6 = Parser6() let xmlParser6 = XMLParser(knowledge: xmlData_text) xmlParser6.delegate = parser6 xmlParser6.parse()
For those who compiled and ran this system, it is best to get this textual content content material:
Getting Began with Swift true Parse XML with Rust false
Dealing with errors when parsing the XML knowledge
Life just isn’t supreme. Typically you get imperfect XML knowledge and the info is corrupted. However don’t fear! You may deal with errors with the parser
methodology with the next signature:
func parser( _ parser: XMLParser, parseErrorOccurred parseError: Error )
However first, it’s essential to write the corrupted XML knowledge:
let xmlContent_corrupted = """ <?xml model="1.0" encoding="UTF-8"?> <root> <article> <title>Getting Began with Swift</title> </article> <article> <title> Parse XML with Rust </article> </root> """; let xmlData_corrupted = Knowledge(xmlContent_corrupted.utf8)
Discover that there’s a lacking closing title
tag.
Then, create a delegated class to deal with the error:
class Parser7 : NSObject, XMLParserDelegate { func parser( _ parser: XMLParser, parseErrorOccurred parseError: Error ) { print(parseError) } }
This methodology gave the parseError
argument that has the details about the error. On this case, you printed the error itself.
What does the error appear like? It’s good to create the XMLParser
occasion to take this delegated class:
let parser7 = Parser7() let xmlParser7 = XMLParser(knowledge: xmlData_corrupted) xmlParser7.delegate = parser7 xmlParser7.parse()
For those who compiled and ran this system, it is best to see the error:
Error Area=NSXMLParserErrorDomain Code=76 "(null)" UserInfo={NSXMLParserErrorColumn=15, NSXMLParserErrorLineNumber=8, NSXMLParserErrorMessage=Opening and ending tag mismatch: title line 7 and article }
You bought the road quantity and column quantity the place the error occurred. You additionally obtained the error message, which advised you that the opening and ending tags have been mismatched.
Conclusion
On this article, you discovered parse XML knowledge. You began by creating the XMLParser
occasion. You then created a delegated XMLParserDelegate
class to deal with the logic of the parsing course of. From there, you dealt with components, attributes, and textual content content material. You additionally managed an error within the parsing course of and configured the parser to deal with the namespace.
Extra nice articles from LogRocket:
This text solely scratches the floor of XML parsing in Swift. You may study extra concerning the XML parsing API in the documentation right here. The code for this text is obtainable on this GitHub repository.