Reputation: 100258
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
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.
Upvotes: 2
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
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