dictionary of arrays

I get the xml data and with the help of SWXMLHash, I convert them into a dictionary of arris, but I don’t understand how to get access to the key - value or specific data. ( USD and EUR value)?

Sorry for the stupid question, I'm just learning

import UIKit
import Alamofire
import SWXMLHash

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

      let date = "02/03/2002"
        Alamofire.request("http://www.cbr.ru/scripts/XML_daily.asp?date_req=\(date)", parameters: nil) //Alamofire defaults to GET requests
            .response { response in
                if let data = response.data {
                    print(data) // if you want to check XML data in debug window.
                    //let xml = SWXMLHash.parse(data)
                    let xml = SWXMLHash.parse(response.data!)
                   // print(xml) // output the FilmID element.
                    func enumerate(indexer: XMLIndexer) {
                        for child in indexer.children {
                            print(child.element!.name, child.element!.text)
                            enumerate(indexer: child)
                        }
                    }
                    enumerate(indexer: xml)
                    
                }
        }
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Replies

Without knowing any information about how your data is organized, it is difficult to tell how to retrieve some specific data from it.


For example, we do not know if your data really contains USD or EUR.


Please show your original xml text, or you may need to try to dump the parsed xml.

If you do not know how to check the original xml text,

please replace `print(data)` on line 14. to `print(String(data: data, encoding: .utf8))` and see what you get.

<ValCurs name="Foreign Currency Market" Date="02.03.2002"><Valute ID="R01010"><NumCode>036</NumCode><CharCode>AUD</CharCode><Nominal>1</Nominal><Name>Австралийский доллар</Name><Value>16,0102</Value></Valute><Valute ID="R01035"><NumCode>826</NumCode><CharCode>GBP</CharCode><Nominal>1</Nominal><Name>Фунт стерлингов Соединенного королевства</Name><Value>43,8254</Value></Valute><Valute ID="R01090"><NumCode>974</NumCode><CharCode>BYR</CharCode><Nominal>1000</Nominal><Name>Белорусских рублей</Name><Value>18,4290</Value></Valute><Valute ID="R01215"><NumCode>208</NumCode><CharCode>DKK</CharCode><Nominal>10</Nominal><Name>Датских крон</Name><Value>36,1010</Value></Valute><Valute ID="R01235"><NumCode>840</NumCode><CharCode>USD</CharCode><Nominal>1</Nominal><Name>Доллар США</Name><Value>30,9436</Value></Valute><Valute ID="R01239"><NumCode>978</NumCode><CharCode>EUR</CharCode><Nominal>1</Nominal><Name>Евро</Name><Value>26,8343</Value></Valute><Valute ID="R01310"><NumCode>352</NumCode><CharCode>ISK</CharCode><Nominal>100</Nominal><Name>Исландских крон</Name><Value>30,7958</Value></Valute><Valute ID="R01335"><NumCode>398</NumCode><CharCode>KZT</CharCode><Nominal>100</Nominal><Name>Казахстанских тенге</Name><Value>20,3393</Value></Valute><Valute ID="R01350"><NumCode>124</NumCode><CharCode>CAD</CharCode><Nominal>1</Nominal><Name>Канадский доллар</Name><Value>19,3240</Value></Valute><Valute ID="R01535"><NumCode>578</NumCode><CharCode>NOK</CharCode><Nominal>10</Nominal><Name>Норвежских крон</Name><Value>34,7853</Value></Valute><Valute ID="R01589"><NumCode>960</NumCode><CharCode>XDR</CharCode><Nominal>1</Nominal><Name>СДР (специальные права заимствования)</Name><Value>38,4205</Value></Valute><Valute ID="R01625"><NumCode>702</NumCode><CharCode>SGD</CharCode><Nominal>1</Nominal><Name>Сингапурский доллар</Name><Value>16,8878</Value></Valute><Valute ID="R01700"><NumCode>792</NumCode><CharCode>TRL</CharCode><Nominal>1000000</Nominal><Name>Турецких лир</Name><Value>22,2616</Value></Valute><Valute ID="R01720"><NumCode>980</NumCode><CharCode>UAH</CharCode><Nominal>10</Nominal><Name>Украинских гривен</Name><Value>58,1090</Value></Valute><Valute ID="R01770"><NumCode>752</NumCode><CharCode>SEK</CharCode><Nominal>10</Nominal><Name>Шведских крон</Name><Value>29,5924</Value></Valute><Valute ID="R01775"><NumCode>756</NumCode><CharCode>CHF</CharCode><Nominal>1</Nominal><Name>Швейцарский франк</Name><Value>18,1861</Value></Valute><Valute ID="R01820"><NumCode>392</NumCode><CharCode>JPY</CharCode><Nominal>100</Nominal><Name>Японских иен</Name><Value>23,1527</Value></Valute></ValCurs>

It is after SWXMLHash

ValCurs

Valute

NumCode 036

CharCode AUD

Nominal 1

Name Австралийский доллар

Value 16,0102

Valute

NumCode 826

CharCode GBP

Nominal 1

Name Фунт стерлингов Соединенного королевства

Value 43,8254

Valute

NumCode 974

CharCode BYR

Nominal 1000

Name Белорусских рублей

Value 18,4290

Valute

NumCode 208

CharCode DKK

Nominal 10

Name Датских крон

Value 36,1010

Valute

NumCode 840

CharCode USD

Nominal 1

Name Доллар США

Value 30,9436

Valute

NumCode 978

CharCode EUR

Nominal 1

Name Евро

Value 26,8343

Valute

NumCode 352

CharCode ISK

Nominal 100

Name Исландских крон

Value 30,7958

Valute

NumCode 398

CharCode KZT

Nominal 100

Name Казахстанских тенге

Value 20,3393

Valute

NumCode 124

CharCode CAD

Nominal 1

Name Канадский доллар

Value 19,3240

Valute

NumCode 578

CharCode NOK

Nominal 10

Name Норвежских крон

Value 34,7853

Valute

NumCode 960

CharCode XDR

Nominal 1

Name СДР (специальные права заимствования)

Value 38,4205

Valute

NumCode 702

CharCode SGD

Nominal 1

Name Сингапурский доллар

Value 16,8878

Valute

NumCode 792

CharCode TRL

Nominal 1000000

Name Турецких лир

Value 22,2616

Valute

NumCode 980

CharCode UAH

Nominal 10

Name Украинских гривен

Value 58,1090

Valute

NumCode 752

CharCode SEK

Nominal 10

Name Шведских крон

Value 29,5924

Valute

NumCode 756

CharCode CHF

Nominal 1

Name Швейцарский франк

Value 18,1861

Valute

NumCode 392

CharCode JPY

Nominal 100

Name Японских иен

Value 23,1527

Please make some effort to format it for readability...


<ValCurs name="Foreign Currency Market" Date="02.03.2002">

<Valute ID="R01010">

<NumCode>036</NumCode>

<CharCode>AUD</CharCode>

<Nominal>1</Nominal>

<Name>Австралийский доллар</Name>

<Value>16,0102</Value>

</Valute>

<Valute ID="R01035">

<NumCode>826</NumCode>

<CharCode>GBP</CharCode>

<Nominal>1</Nominal>

<Name>Фунт стерлингов Соединенного королевства</Name>

<Value>43,8254</Value>

</Valute>

<Valute ID="R01090">

<NumCode>974</NumCode>

<CharCode>BYR</CharCode>

<Nominal>1000</Nominal>

<Name>Белорусских рублей</Name>

<Value>18,4290</Value>

</Valute>

<Valute ID="R01215">

<NumCode>208</NumCode>

<CharCode>DKK</CharCode>

<Nominal>10</Nominal>

<Name>Датских крон</Name>

<Value>36,1010</Value>

</Valute>

<Valute ID="R01235">

<NumCode>840</NumCode>

<CharCode>USD</CharCode>

<Nominal>1</Nominal>

<Name>Доллар США</Name>

<Value>30,9436</Value>

</Valute>

<Valute ID="R01239">

<NumCode>978</NumCode>

<CharCode>EUR</CharCode>

<Nominal>1</Nominal>

<Name>Евро</Name>

<Value>26,8343</Value>

</Valute>

<!-- ... -->

</ValCurs>


So, the outermost element is ValCurs.

Inside ValCurs, there are multiple Valute elements.

Inside Valute, just 5 elements -- NumCode, CharCode, Nominal, Name and Value.


I have never used SWXMLHash, but according to its doc, you can write something like this:


(Outside of the class, somewhere toplevel in your project)

struct Valute: XMLIndexerDeserializable {
    let numCode: Int
    let charCode: String
    let nominal: Int
    let name: String
    let value: Decimal
    
    enum DeserializeError: Error {
        case invalidDecimal
    }
    
    static func deserialize(node: XMLIndexer) throws -> Valute {
        let strValue: String = try node["Value"].value()
        guard let value = Decimal(string: strValue.replacingOccurrences(of: ",", with: ".")) else {
            throw DeserializeError.invalidDecimal
        }
        return try Valute(
            numCode: node["NumCode"].value(),
            charCode: node["CharCode"].value(),
            nominal: node["Nominal"].value(),
            name: node["Name"].value(),
            value: value
        )
    }
}


With the struct above, you can (I believe) write something like this after line 24. of your code

                    let valuteArr: [Valute] = xml["ValCurs"]["Valute"].value()
                    if let usdValute = valuteArr.first(where: {$0.charCode == "USD"}) {
                        print(usdValute)
                    } else {
                        print("No entry for USD")
                    }
                    if let eurValute = valuteArr.first(where: {$0.charCode == "EUR"}) {
                        print(eurValute)
                    } else {
                        print("No entry for USD")
                    }


As I wrote above, I have never used SWXMLHash and you may need some fixes to make the code above work.

But this is all I can do for you now. SWXMLHash is not an Apple's framework, you should better not expect much support in Apple's site.


If you want to use XMLParser instead of SWXMLHash, there would be many who can help you.

import UIKit

class ViewController: UIViewController, XMLParserDelegate {
    
    var numCode: String = ""
    var charCode: String = ""
    var nominal: String = ""
    var name: String = ""
    var value: String = ""
    
    var currentParsingElement:String = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        getXMLDataFromServer()
    }
    func getXMLDataFromServer() {
        let url = NSURL(string: "https://www.cbr.ru/scripts/XML_daily.asp?date_req=02/03/2002")
        
        let task = URLSession.shared.dataTask(with: url! as URL) { (data, response, error) in
            if data == nil {
                print ("dataTaskWhithRequest error: \(String(describing: error?.localizedDescription))")
                return
            }
            let parser = XMLParser(data: data!)
            parser.delegate = self
            parser.parse()
        }
        task.resume()
    }
    
    func parser(_parser: XMLParser,didStartElement elementName: String, namespaceURl: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentParsingElement = elementName
        if elementName == "Response" {
            print ("Started parsing...")
        }
    }
    
    func parser(_parser: XMLParser, foundCharacters string: String) {
        let foundChar = string.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
        
        if (!foundChar.isEmpty) {
            if currentParsingElement == "numCode" {
                numCode += foundChar
            }
            if currentParsingElement == "charCode" {
                charCode += foundChar
            }
            if currentParsingElement == "nominal" {
                nominal += foundChar
            }
            if currentParsingElement == "name" {
                name += foundChar
            }
            if currentParsingElement == "value" {
                value += foundChar
            }
        }
    }
    func parser (_parser: XMLParser, didEndElement elementName: String, namespaceURL: String?, qualifiedName qName: String?) {
        if elementName == "Response" {
            print ("end parsing...")
        }
    }
    func parserDidEndDocument(_ parser: XMLParser) {
        DispatchQueue.main.async {
           // self.displayOnUI()
            self.displayOnUI() 
        }
    }
    func parser(_parser: XMLParser, parseErrorOccurred parseError: Error) {
        print("parseErrorOccurred: \(parseError)")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


thanks for the help}

thanks for the help

I used the XML Parser, how can I see the result of parse?

I used the XML Parser, how can I see the result of parse?


Before seeing your result, you have many, many parts to be fixed. (Seems you have found a wrong tutorial for your XML.)

import UIKit

//### You have multiple Valute elements in your XML, prepare a struct which represents each Valute element
struct Valute {
    let numCode: Int
    let charCode: String
    let nominal: Int
    let name: String
    let value: Decimal
}

class ViewController: UIViewController, XMLParserDelegate {
    
    var numCodeStr: String = "" //### converted to Int later
    var charCode: String = ""
    var nominalStr: String = "" //### converted to Int later
    var name: String = ""
    var valueStr: String = "" //### converted to Decimal later

    //### Common place to put text while parsing text-only elements
    var currentParsingText: String? = nil
    
    //### You have multiple Valute elements in your XML, prepare an Array which contais all Valute elements
    var valuteArr: [Valute] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        getXMLDataFromServer()
    }
    
    func getXMLDataFromServer() {
        //### Use `URL` rather than `NSURL` (or consider using `URLComponents`)
        let dateStr = "02/03/2002"
        guard let url = URL(string: "...(put your right URL here.../XML_daily.asp?date_req=\(dateStr)") else {
            print("URL is wrong")
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else {
                if let error = error {
                    print("dataTaskWhithRequest error: \(error)")
                } else {
                    print("Unexpected nil") //### This may never happen...
                }
                return
            }
            //### Parser adds elements to `valuteArr`, reset it before parsing
            self.valuteArr = []
            let parser = XMLParser(data: data)
            parser.delegate = self
            parser.parse()
            //### Call your UI update here.
            if let error = parser.parserError {
                print(error)
            } else {
                DispatchQueue.main.async {
                    self.displayOnUI()
                }
            }
        }
        task.resume()
    }
    
    //### The parameter label was wrong... (you need a whitespace between `_` and `parser`
    func parser(_ parser: XMLParser,didStartElement elementName: String, namespaceURI namespaceURl: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        //### You need to handle all the element names in `didStartElement`
        //### And there is no element named `Response`
        //### And all the element names are capitalized in your XML
        switch elementName {
        case "ValCurs":
            print ("Found root element...")
        case "Valute":
            //Found start tag of new Valute element, reset all variables for its contents
            numCodeStr = ""
            charCode = ""
            nominalStr = ""
            name = ""
            valueStr = ""
        case "NumCode", "CharCode", "Nominal", "Name", "Value":
            //Found start tag of text-only elements, reset text variable used while parsing the element
            currentParsingText = ""
        default:
            print("Unexected element:", elementName)
        }
    }
    
    //### The parameter label was wrong... (you need a whitespace between `_` and `parser`
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        //### Just add to `currentParsingText`
        currentParsingText? += string
    }
    
    //### The parameter label was wrong... (you need a whitespace between `_` and `parser`
    //### and the third parameter label is `namespaceURI`, not `namespaceURL`
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        //### You need to handle all the element names in `didStartElement`
        //### And there is no element named `Response`
        switch elementName {
        case "ValCurs":
            print ("End root element...")
        case "Valute":
            //### Found end tag of new Valute element, add a new Valute element to `valuteArr`
            guard let numCode = Int(numCodeStr), let nominal = Int(nominalStr),
                let value = Decimal(string: valueStr.replacingOccurrences(of: ",", with: "."))
            else {
                    print("Some entry cannot be parsed as number: NumCode: \(numCodeStr), Nominal: \(nominalStr), value: \(valueStr)")
                    return
            }
            let newValute = Valute(numCode: numCode, charCode: charCode, nominal: nominal, name: name, value: value)
            valuteArr.append(newValute)
        //
        //### At the end of each text-only element, set `currentParsingText` to its own variable
        //
        case "NumCode":
            numCodeStr = currentParsingText ?? ""
            currentParsingText = nil
        case "CharCode":
            charCode = currentParsingText ?? ""
            currentParsingText = nil
        case "Nominal":
            nominalStr = currentParsingText ?? ""
            currentParsingText = nil
        case "Name":
            name = currentParsingText ?? ""
            currentParsingText = nil
        case "Value":
            valueStr = currentParsingText ?? ""
            currentParsingText = nil
        default:
            print("Unexected end element:", elementName)
        }
    }
    
    func parserDidStartDocument(_ parser: XMLParser) {
        print ("Started parsing...")
    }
    
    func parserDidEndDocument(_ parser: XMLParser) {
        //### This is not a good place to update your UI...
        print ("end parsing...")
    }
    
    func parser(_parser: XMLParser, parseErrorOccurred parseError: Error) {
        print("parseErrorOccurred: \(parseError)")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func displayOnUI() {
        //### What do you want to do here?
        //...
        for valute in valuteArr {
            print(valute.charCode, valute.value)
        }
    }
}

Use `valuteArr` as you like, in the `displayOnUI()` method.