Parse Xml into Array (Swift)

how to get array from and xml file in swift

You can write your own XML parser, conforming to NSXMLParser or use a library like HTMLReader:

let fileURL = NSBundle.mainBundle().URLForResource("data", withExtension: "xml")!
let xmlData = NSData(contentsOfURL: fileURL)!
let topic = "OCR_Businessstudies_A_Topics"

let document = HTMLDocument(data: xmlData, contentTypeHeader: "text/xml")
for item in document.nodesMatchingSelector("string-array[name='\(topic)'] item") {
print(item.textContent)
}

Swift Xcode XML string to String of Array

It's not so complicated to use the XMLParser class, specially with this simple structure.

For the delegate we implement 3 of the protocol methods, two to keep track of if the currently parsed object is a project element and one to read the value.

class ParserDelegate: NSObject, XMLParserDelegate {
var projects = [String]()
var isProject = false

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if !isProject, elementName == "project" { isProject = true }
}

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
isProject = false
}

func parser(_ parser: XMLParser, foundCharacters string: String) {
if isProject { projects.append(string)}
}
}

Example

let parser = XMLParser(data: data)
let delegate = ParserDelegate()
parser.delegate = delegate
parser.parse()
print(delegate.projects)

["123 first", "234 second", "345 third"]

Problem decode string from xml file in Swift

First of all, you should better check if your BoiTay.xml is really in UTF-8. I'm not familiar with Vietnamese encodings, but some tools may generate XMLs with other encodings than UTF-8, even if the xml header states encoding="utf-8".

The result seems to be an encoding issue, rather than a bug of the library or your code.

Please show hex dump of your xmlData including the first item element.

print(xmlData as NSData)

Maybe the first 256 bytes would be enough.


By the way, using XMLParser is not so difficult. (Though it is not super-easy.)

Here is an example you can test in the Playground.

import Foundation

class ResoucesParsingDelegate: NSObject, XMLParserDelegate {
//Property to keey all `string-array`s by name
var stringArrays: [String: [String]] = [:]

var nameParsing: String? = nil
var stringArrayParsing: [String]? = nil

var currentText: String? = nil

func parserDidStartDocument(_ parser: XMLParser) {
print(#function)
}

func parserDidEndDocument(_ parser: XMLParser) {
print(#function)
}

func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(#function, parseError)
}

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "string-array":
guard let name = attributeDict["name"] else {
print("`string-array` element needs `name` attribute")
return
}
//When you find `<string-array name="...">`, prepare a string array to keep items with its name
nameParsing = name
stringArrayParsing = []
case "item":
if stringArrayParsing == nil {
print("invalid `item` element")
return
}
//When you find `<item>`, prepare a string to keep the content text of the element
currentText = ""
//Prodess other elements
//...
default:
print("Unknown element `\(elementName)`, ignored")
}
}

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "string-array":
if stringArrayParsing == nil || nameParsing == nil {
print("invalid end tag `string-array`")
return
}
//When you find `</string-array>`, add the current string array to `stringArrays` with its name
stringArrays[nameParsing!] = stringArrayParsing!
//Clear string array for next use
stringArrayParsing = nil
case "item":
if stringArrayParsing == nil || currentText == nil {
print("invalid end tag `item`")
return
}
//When you find `</item>` add the content text to `stringArrayParsing`
stringArrayParsing!.append(currentText!)
//Clear content text for next use
currentText = nil
//Prodess other elements
//...
default:
print("Unknown element `\(elementName)`, ignored")
}
}

func parser(_ parser: XMLParser, foundCharacters string: String) {
if currentText == nil {
//Silently igonore characters while content string is not ready
return
}
currentText! += string
}
}

let xmlText = """
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="array_ngontay_kq">
<item>Bạn là người đáng tin cậy.</item>
<item>Bạn là người có óc xét đoán. </item>
</string-array>
</resources>
"""

let xmlData = xmlText.data(using: .utf8)!

print(xmlData, xmlData as NSData)

let parser = XMLParser(data: xmlData)
let resoucesParsingDelegate = ResoucesParsingDelegate()
parser.delegate = resoucesParsingDelegate
parser.parse()

print(resoucesParsingDelegate.stringArrays)

Output:

246 bytes <3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d227574 662d3822 3f3e0a3c 7265736f 75726365 733e0a20 2020203c 73747269 6e672d61 72726179 206e616d 653d2261 72726179 5f6e676f 6e746179 5f6b7122 3e0a2020 20202020 20203c69 74656d3e 42e1baa1 6e206cc3 a0206e67 c6b0e1bb 9d6920c4 91c3a16e 67207469 6e2063e1 baad792e 3c2f6974 656d3e0a 20202020 20202020 3c697465 6d3e42e1 baa16e20 6cc3a020 6e67c6b0 e1bb9d69 2063c3b3 20c3b363 2078c3a9 7420c491 6fc3a16e 2e203c2f 6974656d 3e0a2020 20203c2f 73747269 6e672d61 72726179 3e0a3c2f 7265736f 75726365 733e>
parserDidStartDocument
Unknown element `resources`, ignored
Unknown element `resources`, ignored
parserDidEndDocument
["array_ngontay_kq": ["Bạn là người đáng tin cậy.", "Bạn là người có óc xét đoán. "]]

If you test this code with the contents of your BoiTay.xml and get the similar result as HTMLReader, the problem definitely is an issue of encoding.

(You may need to modify this code if your actual xml is more complex than the example.)

Parsing xml from from url in iOS Swift

It isn't entirely clear from your question what your goal is, but I assume it is to extract the attributes of the result elements contained in the CreditCheckMSResult element.

The values are attributes of the result element, not children. So, when you get the start of the result element in didStartElement, the values you want are in the attributes dictionary that is passed to that function.

A dictionary is rarely a good final data model. In this case I would suggest that you create an array of structs to contain your data.

struct CreditCheckResult {
let message: String
let ref: String
let network: String
}

The xmlns attribute is to detail the XML namespace that apples to the element, if there is one. You wouldn't want to store this.

Since you aren't interested in the children of the result element (it doesn't have any). You pretty much only need the didStart delegate methods.

let recordKey = "result"
let results = [CreditCheckResult]?
extension ViewController: XMLParserDelegate {

func parserDidStartDocument(_ parser: XMLParser) {
results = []
}

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

// We only care about "result" elements
guard elementName == recordKey else {
return
}

guard let message = attributes["message"],
let ref = attributes["ref"],
let network = attributes["network"] else {
print("Malformed result element = required attribute missing")
return
}

self.results?.append(CreditCheckResult(message: message, ref: ref, network:network))
}

func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError)
results = []
}

}

Swift 5 Alamofire 5 - Parse XML

You can use XMLMapper. Your model will look like this:

struct DataSet: XMLMappable {
var nodeName: String!

var cursValutar: CursValutar?

init?(map: XMLMap) {}

mutating func mapping(map: XMLMap) {
cursValutar <- map["Body.Cube"]
}
}

struct CursValutar: XMLMappable {
var nodeName: String!

var curs: [Currency]?

init?(map: XMLMap) {}

mutating func mapping(map: XMLMap) {
curs <- map["Rate"]
}
}

struct Currency: XMLMappable {
var nodeName: String!

var symbol: String?
var value: Double?

init?(map: XMLMap) {}

mutating func mapping(map: XMLMap) {
symbol <- map.attributes["currency"]
value <- map.innerText
}
}

You can fetch and map this XML like that:

AF.request(url).responseString { (dataResponse: AFDataResponse<String>) in
switch dataResponse.result {
case .success(let xmlString):
let dataSet = XMLMapper<DataSet>().map(XMLString: xmlString)
print(dataSet?.cursValutar?.curs?.first?.value)
case .failure(let error):
print(error)
}
}


Related Topics



Leave a reply



Submit