在PHP中使用DOMXPath获取XML属性,忽略名称空间


Getting an XML attribute with DOMXPath in PHP ignoring namespaces

我有一个XML文件:

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

,我尝试在PHP中使用XPath获取CertificateId:

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

$id是空的,因为Msft已经放弃了这个链接,所以名称空间是不可访问的,也就是说,我必须得到CertificateId忽略名称空间(如果我从XML中删除名称空间字符串,我的代码工作,但我宁愿不编辑XML)。我知道这可以用local-name() XPath函数完成,但我如何在PHP中做到这一点?


下面是我从DOM中读取它的方法:

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

这段代码忽略名称空间。但是如何使用XPath做到这一点呢?

在XPath 2.0中,可以对名称空间部分使用通配符:string(//*:Receipt/@CertificateId)。但是,据我所知,PHP目前只实现了XPath 1.0。

您可以注册命名空间:

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

或者只选择属性,如果您甚至不想提及特定的名称空间:

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

否则,您将被迫使用local-name:

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

这里不需要忽略名称空间。名称空间不必是现有的url。它们必须是全局唯一的urn。许多人将URL与他们拥有的域名一起使用,这样就不会有太多冲突的机会。你可以在那里放一些文档

要在Xpath中使用名称空间,只需为它注册一个前缀:

$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)')
);
输出:

string(40) "A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD"

URN命名空间的示例为rfc6321

如果您不喜欢使用Xpath(无法想象为什么)。你可以使用感知命名空间的DOM方法和属性。

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