Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

LINQ to XML - namespaces 1

Status
Not open for further replies.

rvBasic

Programmer
Oct 22, 2000
414
0
0
BE
By trial and error, I managed to extract exchange rates from the official ECB (European Central Bank) website;

Following piece of code was used:
Code:
Imports System.Xml.Linq
Imports <xmlns:gesmes="[URL unfurl="true"]http://www.gesmes.org/xml/2002-08-01">[/URL]
Imports <xmlns="[URL unfurl="true"]http://www.ecb.int/vocabulary/2002-08-01/eurofxref">[/URL]


Module Module1

    Sub Main()

        'Dim strXrateUrl As String = "[URL unfurl="true"]http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"[/URL]
        'Dim docXrate As XDocument = XDocument.Load(strXrateUrl)
        'Console.WriteLine(docXrate.ToString)

        Dim strEcbRates = _
          <?xml version="1.0" encoding="UTF-8"?>
          <gesmes:Envelope xmlns:gesmes="[URL unfurl="true"]http://www.gesmes.org/xml/2002-08-01"[/URL] xmlns="[URL unfurl="true"]http://www.ecb.int/vocabulary/2002-08-01/eurofxref">[/URL]
              <gesmes:subject>Reference rates</gesmes:subject>
              <gesmes:Sender>
                  <gesmes:name>European Central Bank</gesmes:name>
              </gesmes:Sender>
              <Cube>
                  <Cube time="2010-07-14">
                      <Cube currency="USD" rate="1.2703"/>
                      <Cube currency="JPY" rate="112.49"/>
                      <Cube currency="BGN" rate="1.9558"/>
                      <Cube currency="CZK" rate="25.470"/>
                      <Cube currency="THB" rate="41.050"/>
                      <Cube currency="ZAR" rate="9.5960"/>
                  </Cube>
              </Cube>
          </gesmes:Envelope>

        Dim root = strEcbRates.Element("{[URL unfurl="true"]http://www.gesmes.org/xml/2002-08-01}Envelope")[/URL]

        For Each el In root.Elements
            'Search and retain the outer Cube element
            Select Case el.Name = "{[URL unfurl="true"]http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube"[/URL]
                Case True
                    Dim xelCube = el 'Outer Cube, has one dependent element, also named Cube
                    'it has a Time attribute (which is a date!)
                    Dim xelTime = xelCube.Element("{[URL unfurl="true"]http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube")[/URL]
                    'Elements with exchange rates are also called Cube!

                    Dim EuroToX = _
                    From x In xelTime.Elements _
                    Select New With {.Currency = x.Attribute("currency").Value, _
                                     .XRate = x.Attribute("rate").Value}


                Case False
                    'Do Nothing
            End Select
        Next

    End Sub

End Module

Access to the life website has been commented out and replaced by a Dim definition, so that one can see how the website looks like.

I have two problems with this "solution":
1. As you can see, the element names are fully qualified. However, I got hopelessly entangled while trying to use namespaces properly in VB (by imporing them and then trying to use their prefixes)
2. the variable EurToX does not seem to exist outside the major For .. root.elements loop.

Can somebody sort this out?

_________________________________
In theory, there is no difference between theory and practice. In practice, there is. [attributed to Yogi Berra]
 
>1. As you can see, the element names are fully qualified. However, I got hopelessly entangled while trying to use namespaces properly in VB (by imporing them and then trying to use their prefixes)
[A1] I think that is the proper way to handle namespace, only that it seems to be a bit clumsy that annoys. The built-in way is to use XNamespace class for that purpose (I will try to show how it enters into the picture.)

>2. the variable EurToX does not seem to exist outside the major For .. root.elements loop.
[A2] That you cannot overcome as such, as it is the scoping of variables. You can use a hashtable to collect the data and use outside the loop as long as the hashtable is declared outside of it as well. (I will again try to show how it would look like.)

[3] I would try to re-write the lower part to illustrate how the logic actually runs. (I put an intermediate linq to select the special date, ie attribute time, to make the process more realistic in the actual purpose.) (However, I think more of c#, so my mental transcription may not be perfect...)
[tt]
dim ns as XNamespace="[ignore][/ignore]"
dim ns2 as XNamespace="[ignore][/ignore]"
Dim root = strEcbRates.Element(ns+"Envelope")
dim query=from nodelist in root.Elements(ns2+"Cube").Elements(ns2+"Cube") _
where nodelist.Attribute("time").Value="2010-07-14" _
select nodelist

for each k in query
Console.WriteLine("date: "+k.Attribute("time").Value)
dim query2=from nodelist2 in k.Elements(ns2+"Cube") select nodelist2

dim ht as Hashtable=new Hashtable() 'scope restrict to each date (attribute time)

for each k2 in query2
'Console.WriteLine(k2.Attribute("currency").Value+"="+k2.Attribute("rate").Value)
'natural assumption: currency symbol appeared being unique, otherwise, know what to do.
ht.Add(k2.Attribute("currency").Value,k2.Attribute("rate").Value)
next

for each de as DictionaryEntry in ht
Console.WriteLine("currency={0}, rate={1}", de.Key, de.Value)
next
next
[/tt]
 
Thanks tsuji: Your post was a major breakthrough for me. It was not at all obvious to me how handle the namespaces prefixes. I finally came up with the following code (and I apologize for not blindly copying yours [smile])

Code:
Imports System.Xml.Linq

Module Module1

    Sub Main()

        Dim strXrateUrl As String = "[URL unfurl="true"]http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"[/URL]
        Dim docXrate As XDocument = XDocument.Load(strXrateUrl)

        Dim gesmes As XNamespace = "[URL unfurl="true"]http://www.gesmes.org/xml/2002-08-01"[/URL]
        Dim ecbxref As XNamespace = "[URL unfurl="true"]http://www.ecb.int/vocabulary/2002-08-01/eurofxref"[/URL]

        'exchange rates will be dropped into this hashtable, with the curency symbol as the key
        Dim hshXRate As Hashtable
        hshXRate = New Hashtable

        'at the ECB website, the rates are expressed with a decimal point, which may
        'be in conflict with the local format
        'Get the local format
        Dim strLocalDecimalSeparator As String
        strLocalDecimalSeparator = My.Computer.Info.InstalledUICulture.NumberFormat.NumberDecimalSeparator

        Dim root = docXrate.Element(gesmes + "Envelope")

        For Each el In root.Elements
            'Search and retain the outer Cube element
            Select Case el.Name = ecbxref + "Cube"
                Case True
                    Dim xelCube = el 'Outer Cube, has one dependent element, also named Cube
                    'it has a Time attribute (which is a date!)
                    Dim xelTime = xelCube.Element(ecbxref + "Cube")
                    'Elements with exchange rates are also called Cube!

                    For Each item In xelTime.Elements

                        Dim strXRate As String
                        Select Case strLocalDecimalSeparator
                            Case "."
                                strXRate = item.Attribute("rate").Value
                            Case ","
                                strXRate = Replace(item.Attribute("rate").Value, ".", ",")
                        End Select

                        [COLOR=blue]hshXRate.Add(item.Attribute("currency").Value, CType(strXRate, Single))[/color]

                    Next
                Case False
                    'Do Nothing
            End Select
        Next

        'List the exchange rates from the hash table
        Dim dicE As IDictionaryEnumerator
        dicE = hshXRate.GetEnumerator
        While dicE.MoveNext
            Console.WriteLine(dicE.Key.ToString & "=" & dicE.Value.ToString)
        End While

    End Sub

End Module

I still have a minor problem with this code: the compiler flags line 44 (the blue line) with the following warning:
Variable 'strXRate' is used before it has been assigned a value. A null reference exception could result at runtime.
Although the code runs fine, I must have overlooked something, but I don't know what?

PS: I hav'nt decided yet how to handle the 'time' attribute. I will probably attach it to a Property of the class that will eventually contain the hashtable.

_________________________________
In theory, there is no difference between theory and practice. In practice, there is. [attributed to Yogi Berra]
 
The clever compiler assumes that a strLocalDecimalSeparator other than "." or "," may be presented at run time, therefore it wants an Else clause in the Select statement to cover that possibility.

I changed that Select to
Code:
                        Select Case strLocalDecimalSeparator
                            Case "."
                                strXRate = item.Attribute("rate").Value
                            Case ","
                                strXRate = item.Attribute("rate").Value.Replace(".", ",")
                            [COLOR=blue]Case Else
                                strXRate = item.Attribute("rate").Value[/color]
                        End Select

and the Warning disappeared...

_________________________________
In theory, there is no difference between theory and practice. In practice, there is. [attributed to Yogi Berra]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top