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

时间:2015-09-03 10:43:24

标签: php xml xpath simplexml

这是由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)

1 个答案:

答案 0 :(得分:1)

不要从文档中获取命名空间。在您的应用程序中定义它们名称空间是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表达式,可以返回节点列表或标量,具体取决于表达式。

$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"