PHP - SimpleXMLElement 无法正确解析名称空间

PHP - SimpleXMLElement not parsing correctly with namespaces

这是由API返回的:

<?xml version='1.0' encoding='utf-8'?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xml:base="https://exmple.com/odata/">
    <id>https://example.com/odata/PicklistOption(989L)</id>
    <title type="text" />
    <updated>2015-09-03T11:56:51Z</updated>
    <author>
        <name />
    </author>
    <link rel="edit" title="PicklistOption" href="PicklistOption(989L)" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/childPicklistOptions" type="application/atom+xml;type=feed" title="childPicklistOptions" href="PicklistOption(989L)/childPicklistOptions" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/parentPicklistOption" type="application/atom+xml;type=entry" title="parentPicklistOption" href="PicklistOption(989L)/parentPicklistOption" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/picklistLabels" type="application/atom+xml;type=feed" title="picklistLabels" href="PicklistOption(989L)/picklistLabels" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/picklist" type="application/atom+xml;type=entry" title="picklist" href="PicklistOption(989L)/picklist" />
    <category term="SFOData.PicklistOption" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
        <m:properties>
            <d:id m:type="Edm.Int64">989</d:id>
            <d:status>ACTIVE</d:status>
            <d:sortOrder m:type="Edm.Int32">229</d:sortOrder>
            <d:minValue m:type="Edm.Double">-1</d:minValue>
            <d:externalCode>PL</d:externalCode>
            <d:optionValue m:type="Edm.Double">-1</d:optionValue>
            <d:maxValue m:type="Edm.Double">-1</d:maxValue>
        </m:properties>
    </content>
</entry>

正在尝试获取 <d:id>

$xml = new SimpleXMLElement($xmlstr);
$namespaces = $xml->getNameSpaces(true);
$xml->registerXPathNamespace('m', $namespaces['m']);
$xml->registerXPathNamespace('d', $namespaces['d']);

$id = $xml->xpath('/entry/content/m:properties/d:id');
var_dump($id);

但我得到 array(0)

不要从文档中获取命名空间。在您的应用程序中定义它们。命名空间是 xmlns/xmlns:* 属性的值。 xmlns 属性是默认命名空间。所以标签 entry 实际上是 {http://www.w3.org/2005/Atom}:entry.

命名空间必须是唯一的。为避免冲突,大多数人使用 URL。 (其他人不太可能使用您的域来定义他们的名称空间。)这样做的缺点是名称空间是带有特殊字符的大字符串。这可以通过使用名称空间前缀作为别名来解决。

Xpath 没有默认命名空间。您需要为您喜欢使用的每个命名空间注册一个前缀。 Xpath引擎会将前缀解析为实际的命名空间,并将其与解析后的节点命名空间进行比较。

$xml = new SimpleXMLElement($xmlstr);
$namespaces = [
  'a' => 'http://www.w3.org/2005/Atom',
  'm' => 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata',
  'd' => 'http://schemas.microsoft.com/ado/2007/08/dataservices',
  'o' => 'https://exmple.com/odata/'
];
foreach ($namespaces as $prefix => $namespace) {
  $xml->registerXPathNamespace($prefix, $namespace);
}

$id = $xml->xpath('/a:entry/a:content/m:properties/d:id');
var_dump($id);

输出:

array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (0) {
  }
}

您必须在每个 SimpleXMLElement 上再次注册 Xpath 命名空间。

这在 DOM 中更方便。 DOMXpath::evaluate() 执行 Xpath 表达式并且可以 return 节点列表或标量,具体取决于表达式。

$document = new DOMDocument($xmlstr);
$document->loadXml($xmlstr);
$xpath = new DOMXpath($document);
$namespaces = [
  'a' => 'http://www.w3.org/2005/Atom',
  'm' => 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata',
  'd' => 'http://schemas.microsoft.com/ado/2007/08/dataservices',
  'o' => 'https://exmple.com/odata/'
];
foreach ($namespaces as $prefix => $namespace) {
  $xpath->registerNamespace($prefix, $namespace);
}

$id = $xpath->evaluate('string(/a:entry/a:content/m:properties/d:id)');
var_dump($id);

输出:

string(3) "989"