2

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?

2
  • "I know it can be done by local-name() function of xpath, but how can I do this in php?" Why/How you can not use local-name() in PHP? Commented Nov 12, 2016 at 10:11
  • could not find info how to do this, tricky syntax Commented Nov 12, 2016 at 10:12

2 Answers 2

1

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)";
Sign up to request clarification or add additional context in comments.

3 Comments

No, I don't want to even know what it makes me to check. I want just ignore. See my update.
I need to check only the Receipt element. //*/@CertificateId shows attr value of any element, tried /Receipt/*/@CertificateId - doesn't work. And yes, php doesn't support xpath 2.0
Thanks, that works! Not the most obvious syntax, this could be in php manual. Btw, works even with one starting slash.
1

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);

2 Comments

again, I even don't want to know what in this buggy old stuff, I assume several attributes exist, if no - skipping. but that broken namespace almost made me make use of regexps
The namespace is not broken, it does not have to be an existing URL. It works exactly the same for any other namespace.

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.