Tertium
Tertium

Reputation: 6308

Getting an XML attribute with DOMXPath in PHP ignoring namespaces

I have an XML file:

<Receipt Version="2.0" ReceiptDate="2012-08-30T23:10:05Z" CertificateId="A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD" xmlns="http://schemas.microsoft.com/windows/2012/store/receipt">
....
</Receipt>

and I try to get CertificateId with XPath in PHP:

$doc = new DOMDocument();
$doc->loadXML($v);
$xpath = new DOMXPath($doc);
$query = 'string(/Receipt/@CertificateId)';
$id = $xpath->evaluate($query);

$id is void because Msft has abandoned this link, so the namespace is not accessible, that is I have to get CertificateId ignoring namespace (if I remove namespace string from XML, my code works, but I'd prefer to not edit XML). I know it can be done with local-name() XPath function, but how can I do this in PHP?


Here's how I read it from DOM:

        $id = null;
        if ($doc->childNodes->length > 0)
        {
            $root = $doc->childNodes->item(0);
            if ($root->nodeName == 'Receipt')
                $id = $root->attributes->getNamedItem('CertificateId')->nodeValue;
        }

This code ignores namespaces. But how to do this using XPath?

Upvotes: 2

Views: 1033

Answers (2)

ThW
ThW

Reputation: 19502

Here is no need to ignore the namespace. Namespaces do not have to be existing URLs. They have to be globally unique URNs. Many people use an URL with an domain they own, not much chance of an conflict that way. And you can put some documentation there.

To use an namespace in Xpath, just register a prefix for it:

$xml = <<<'XML'
<Receipt 
  Version="2.0" 
  ReceiptDate="2012-08-30T23:10:05Z" 
  CertificateId="A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD" 
  xmlns="http://schemas.microsoft.com/windows/2012/store/receipt">
</Receipt>
XML;

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace(
  'sr', 'http://schemas.microsoft.com/windows/2012/store/receipt'
);
var_dump(
  $xpath->evaluate('string(//sr:Receipt/@CertificateId)')
);

Output:

string(40) "A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD"

An example for an URN namespace is rfc6321

If you don't like to use Xpath (Can't imagine why). You can use the namespace aware DOM methods and properties.

$id = null;
if ($document->childNodes->length > 0) {
  $node = $document->childNodes->item(0);
  if (
    $node->localName == 'Receipt' && 
    $node->namespaceURI == 'http://schemas.microsoft.com/windows/2012/store/receipt'
  ) {
    $id = $node->getAttributeNS(null, 'CertificateId');
  }
}
var_dump($id);

Upvotes: 1

Ruslan Osmanov
Ruslan Osmanov

Reputation: 21492

In XPath 2.0 you might use a wildcard for the namespace part: string(//*:Receipt/@CertificateId). However, PHP currently implements only XPath 1.0, as far as I know.

You can either register the namespace:

$xpath->registerNamespace('x', 'http://schemas.microsoft.com/windows/2012/store/receipt');
$query = 'string(//x:Receipt/@CertificateId)';

or select only attributes, if you don't want to even mention specific namespace:

$query = 'string(//*/@CertificateId)';

Otherwise, you are forced to use local-name:

$query = "string(//*[local-name(.) = 'Receipt']/@CertificateId)";

Upvotes: 1

Related Questions