3

I have this XML from http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
    <gesmes:subject>Reference rates</gesmes:subject>
    <gesmes:Sender>
        <gesmes:name>European Central Bank</gesmes:name>
    </gesmes:Sender>
    <Cube>
        <Cube time='2013-08-23'>
            <Cube currency='USD' rate='1.3355'/>
            <Cube currency='GBP' rate='0.85910'/>
            <Cube currency='HUF' rate='298.98'/>
        </Cube>
    </Cube>
</gesmes:Envelope>

(I removed some values for demonstrational purposes)

I want to get the conversion rate, for lets say, GBP using PHP.

HI can load it using simplexml like this:

$XML=simplexml_load_file("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

    foreach($XML->Cube->Cube->Cube as $rate){
...

But I would like to get the value without iterating, and i really dont want to use regex...

I tried something like this but it didnt work:

$sxe=simplexml_load_file("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

$sxe->registerXPathNamespace('c', 'http://www.gesmes.org/xml/2002-08-01');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');
4
  • didn't work in which way, exactly? Commented Aug 26, 2013 at 21:24
  • i just got an empty array back Commented Aug 29, 2013 at 8:57
  • maybe because there is no 'RUB' in your XML? Commented Aug 29, 2013 at 18:48
  • sorry, there is. i left out some lines to make it shorter and deleted exactly the one with RUB, i changed it to USD... Commented Aug 30, 2013 at 9:59

1 Answer 1

4
+50

The Cube elements are not in the http://www.gesmes.org/xml/2002-08-01 namespace, which has been given the prefix gesmes, they are in the default namespace, which is http://www.ecb.int/vocabulary/2002-08-01/eurofxref.

Therefore rather than:

$sxe->registerXPathNamespace('c', 'http://www.gesmes.org/xml/2002-08-01');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');

You need to register the other namespace:

$sxe->registerXPathNamespace('c', 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');

Here is a live demo showing a result count of 1 with the corrected namespace.


To attempt to give this an authoritative explanation, consider the following excerpts from the Namespaces in XML specification:

The Prefix provides the namespace prefix part of the qualified name, and MUST be associated with a namespace URI reference in a namespace declaration [...] Note that the prefix functions only as a placeholder for a namespace name.

The expanded name corresponding to a prefixed element or attribute name has the URI to which the prefix is bound as its namespace name [...]

A default namespace declaration applies to all unprefixed element names within its scope.

If there is a default namespace declaration in scope, the expanded name corresponding to an unprefixed element name has the URI of the default namespace as its namespace name. If there is no default namespace declaration in scope, the namespace name has no value. [...] In all cases, the local name is [...] the same as the unprefixed name itself.

The element Cube has no prefix, so we look for a default namespace declaration that is in scope; reaching the outermost element, we find xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref", so the "namespace name" of the Cube element is the URI http://www.ecb.int/vocabulary/2002-08-01/eurofxref, and the "local name" is Cube.

If we looked instead at the element gesmes:Sender, we would see that it has a prefix, gesmes, so we would look for a definition of that prefix. Finding xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01", we would conclude that the "expanded name" has the "namespace name" http://www.gesmes.org/xml/2002-08-01, and the "local name" Sender.

In order to use these "namespace names" with XPath, we have to assign prefixes for use in the XPath expression, which needn't correspond to the prefixes in the actual document. In this case, we chose to assign the namespace http://www.ecb.int/vocabulary/2002-08-01/eurofxref the prefix c, so that the expression //c:Cube will match any element with a "namespace name" of http://www.ecb.int/vocabulary/2002-08-01/eurofxref and a "local name" of Cube.

Sign up to request clarification or add additional context in comments.

2 Comments

it works, awesome! i dont understand exactly how the gesmes is not relevant, because it is even in the root node... the two namespaces confuse me i need to read up on the theory behind that! thanks!
Each element is in exactly one namespace, regardless of what its ancestors are in. In this document, nodes prefixed gesmes: are in one namespace, and those with no prefix are in another (the "default" namespace). Using XPath, you need specify only the namespace of the element you're looking for, regardless of what else the document contains.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.