为什么这个 PHP SimpleXML XPath 找不到值?
Why doesn't this PHP SimpleXML XPath find the values?
我期待一个 Amount
的数组,但由于某种原因它被拒绝找到。我尝试了多种设置名称 space 的组合,但它给出了错误 SimpleXMLElement::xpath(): Undefined namespace prefix
.
Code
// $xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
// $item->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
// $possiblePrices = $item->OfferSummary->xpath('//amazon:Amount');
$possiblePrices = $item->OfferSummary->xpath('//Amount');
Yii::error(print_r($item->OfferSummary->asXML(), true));
Yii::error(print_r($possiblePrices, true));
Log
2015-04-15 21:46:06 [::1][-][-][error][application] <OfferSummary><LowestNewPrice><Amount>1</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>[=12=].01</FormattedPrice></LowestNewPrice><TotalNew>15</TotalNew><TotalUsed>0</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary>
in /cygdrive/c/Users/Chloe/workspace/xxx/controllers/ShoppingController.php:208
in /cygdrive/c/Users/Chloe/workspace/xxx/controllers/ShoppingController.php:106
2015-04-15 21:46:06 [::1][-][-][error][application] Array
(
)
Top of XML
<?xml version="1.0" ?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Amount
元素位于命名空间 <http://webservices.amazon.com/AWSECommerceService/2011-08-01>
中。但是在您的 xpath 查询中,您根本没有将它放在命名空间中。所以这个例子将 return 0 个元素:
$xml = simplexml_load_string($buffer);
$result = $xml->xpath('//Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
输出很短:
Result (0):
相反,您需要告知您想要该特定命名空间中的 Amount
元素。您可以通过注册名称空间前缀并在您的 xpath 查询中使用它来做到这一点:
$xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
$result = $xml->xpath('//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
现在输出包含所有这些元素:
Result (24):
<Amount>217</Amount>
<Amount>1</Amount>
<Amount>800</Amount>
<Amount>1</Amount>
<Amount>498</Amount>
...
请注意,如果您在 xpath 查询中要检查多个元素名称,则需要为每个元素指定正确的名称空间。示例:
$result = $xml->xpath('/*/amazon:Items/amazon:Item//amazon:Amount');
您也可以在此处在线找到示例代码:https://eval.in/314347 (backup)-否则我的回答中的示例减去 XML 一目了然:
$xml = simplexml_load_string($buffer);
$result = $xml->xpath('//Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
$xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
$result = $xml->xpath('//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
$result = $xml->xpath('/*/amazon:Items/amazon:Item//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
进一步阅读
- Parse XML with Namespace using SimpleXML
- XPath in SimpleXML for default namespaces without needing prefixes
如果您不想一遍又一遍地注册命名空间前缀,您可以从 SimpleXMLElement 扩展并管理您自己的注册列表:
/**
* Class MyXml
*
* Example on how to register xpath namespace prefixes within the document
*/
class MyXml extends SimpleXMLElement
{
public function registerXPathNamespace($prefix, $ns)
{
$doc = dom_import_simplexml($this)->ownerDocument;
$doc->__sr = $doc;
$doc->__d[] = [$prefix, $ns];
}
public function xpath($path)
{
$doc = dom_import_simplexml($this)->ownerDocument;
if (isset($doc->__d)) {
foreach ($doc->__d as $v) {
parent::registerXPathNamespace($v[0], $v[1]);
}
}
return parent::xpath($path);
}
}
有了这个 MyXml class 你可以加载文档然后你只需要注册一次命名空间:
$namespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
$xml = simplexml_load_file('so-29664051.xml', 'MyXMl');
$xml->registerXPathNamespace("amazon", $namespace);
$item = $xml->Items->Item[0];
$result = $item->xpath('.//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
我期待一个 Amount
的数组,但由于某种原因它被拒绝找到。我尝试了多种设置名称 space 的组合,但它给出了错误 SimpleXMLElement::xpath(): Undefined namespace prefix
.
Code
// $xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
// $item->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
// $possiblePrices = $item->OfferSummary->xpath('//amazon:Amount');
$possiblePrices = $item->OfferSummary->xpath('//Amount');
Yii::error(print_r($item->OfferSummary->asXML(), true));
Yii::error(print_r($possiblePrices, true));
Log
2015-04-15 21:46:06 [::1][-][-][error][application] <OfferSummary><LowestNewPrice><Amount>1</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>[=12=].01</FormattedPrice></LowestNewPrice><TotalNew>15</TotalNew><TotalUsed>0</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary>
in /cygdrive/c/Users/Chloe/workspace/xxx/controllers/ShoppingController.php:208
in /cygdrive/c/Users/Chloe/workspace/xxx/controllers/ShoppingController.php:106
2015-04-15 21:46:06 [::1][-][-][error][application] Array
(
)
Top of XML
<?xml version="1.0" ?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Amount
元素位于命名空间 <http://webservices.amazon.com/AWSECommerceService/2011-08-01>
中。但是在您的 xpath 查询中,您根本没有将它放在命名空间中。所以这个例子将 return 0 个元素:
$xml = simplexml_load_string($buffer);
$result = $xml->xpath('//Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
输出很短:
Result (0):
相反,您需要告知您想要该特定命名空间中的 Amount
元素。您可以通过注册名称空间前缀并在您的 xpath 查询中使用它来做到这一点:
$xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
$result = $xml->xpath('//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
现在输出包含所有这些元素:
Result (24):
<Amount>217</Amount>
<Amount>1</Amount>
<Amount>800</Amount>
<Amount>1</Amount>
<Amount>498</Amount>
...
请注意,如果您在 xpath 查询中要检查多个元素名称,则需要为每个元素指定正确的名称空间。示例:
$result = $xml->xpath('/*/amazon:Items/amazon:Item//amazon:Amount');
您也可以在此处在线找到示例代码:https://eval.in/314347 (backup)-否则我的回答中的示例减去 XML 一目了然:
$xml = simplexml_load_string($buffer);
$result = $xml->xpath('//Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
$xml->registerXPathNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");
$result = $xml->xpath('//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
$result = $xml->xpath('/*/amazon:Items/amazon:Item//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}
进一步阅读
- Parse XML with Namespace using SimpleXML
- XPath in SimpleXML for default namespaces without needing prefixes
如果您不想一遍又一遍地注册命名空间前缀,您可以从 SimpleXMLElement 扩展并管理您自己的注册列表:
/**
* Class MyXml
*
* Example on how to register xpath namespace prefixes within the document
*/
class MyXml extends SimpleXMLElement
{
public function registerXPathNamespace($prefix, $ns)
{
$doc = dom_import_simplexml($this)->ownerDocument;
$doc->__sr = $doc;
$doc->__d[] = [$prefix, $ns];
}
public function xpath($path)
{
$doc = dom_import_simplexml($this)->ownerDocument;
if (isset($doc->__d)) {
foreach ($doc->__d as $v) {
parent::registerXPathNamespace($v[0], $v[1]);
}
}
return parent::xpath($path);
}
}
有了这个 MyXml class 你可以加载文档然后你只需要注册一次命名空间:
$namespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
$xml = simplexml_load_file('so-29664051.xml', 'MyXMl');
$xml->registerXPathNamespace("amazon", $namespace);
$item = $xml->Items->Item[0];
$result = $item->xpath('.//amazon:Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
echo $node->asXML(), "\n";
}