abatishchev
abatishchev

Reputation: 100258

Why my XPath request to XML file on web doesn't work?

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"

Upvotes: 0

Views: 1104

Answers (3)

Cheeso
Cheeso

Reputation: 192467

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

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500495

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.

Upvotes: 5

peter.murray.rust
peter.murray.rust

Reputation: 38043

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="")

Upvotes: 4

Related Questions