views:

177

answers:

3

There is an XML file with exchange rates http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml:

<gesmes:Envelope>
  <gesmes:subject>Reference rates</gesmes:subject>
  <gesmes:Sender>
    <gesmes:name>European Central Bank</gesmes:name>
  </gesmes:Sender>
  <Cube>
    <Cube time="2009-11-26">
      <Cube currency="USD" rate="1.5071"/>
       ...

I do next XPath request:

var doc = new XmlDocument();
doc.Load(url);
var node = doc.SelectSingleNode("//Cube[@currency=\"USD\""]);
var value = node.Attributes["rate"].Value;

but it returns null! Where is my mistake?

If I do this request, all works fine:

var node = dic.SelectSingleNode("//*[@currency=\"USD\"]");
var name = node.Name; // "Cube"
+4  A: 

The problem is the namespace. If you can use LINQ to XML, you can express this query quite easily. Otherwise, it slightly trickier - you'd want something like this:

var doc = new XmlDocument();
doc.Load(url);
XPathNavigator navigator = doc.CreateNavigator();    
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("gesmes", "http://www.gesmes.org/xml/2002-08-01");
nsMgr.AddNamespace("ns0", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");

var node = doc.SelectSingleNode("//ns0:Cube[@currency=\"USD\""], nsMgr);
var value = node.Attributes["rate"].Value;

(You don't really need the gesmes namespace in the manager there, but it'll make it easier if you need to look up any other elements.)

EDIT: Peter Murray-Rust's answer is a nice alternative here - which approach you take depends on how specific you want to be able the element to find. If you only need the namespace for one query, it makes sense to include the URI directly in the XPath; if you'll need it for more than that, you'll get more concise queries using the namespace manager.

Jon Skeet
+4  A: 

Try

var node = doc.SelectSingleNode("//*[local-name()='Cube' and @currency=\"USD\""]);

you can always add in the namespace if you know it

var node = doc.SelectSingleNode("//*[local-name()='Cube' and 
     namespace-uri()='http://www.ecb.int/vocabulary/2002-08-01/eurofxref' and
     @currency=\"USD\""]);

Although it's longwinded I prefer it to trying to sort out namespace prefixes. And it also avoids the problem of the default namespace (xmlns="")

peter.murray.rust
Thanks! It works! Btw, I think it's better to use the same symbol for quoting in XPath: quotes " or apostrophe '
abatishchev
Good - I would normally use '...' but left in your quotes so as not to confuse readers. Personally I find \" as I use Java and the backslashes multiply quite quickly
peter.murray.rust
+2  A: 

XPathVisualizer can be handy. It's free. It wouldn't have told you to use the namespace, but it would have put the namespace in front of you in the UI, and it would havve let you test a bunch of alternatives, really quickly.

alt text

Cheeso
Thanks! Looks cool! I will use it.
abatishchev