【问题标题】:Why doesn't this PHP SimpleXML XPath find the values?为什么这个 PHP SimpleXML XPath 找不到值?
【发布时间】:2015-06-22 05:31:08
【问题描述】:

我期待Amount 的数组,但由于某种原因它拒绝被发现。我尝试了许多设置名称空间的组合,但它给出了错误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>$0.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">

【问题讨论】:

  • 在致电asXML()之前,您能否发布更完整的原始 XML 示例
  • 这里是完整的 XML pastebin.com/U5ar3UQh
  • 这似乎解决了您正在处理的默认命名空间(没有元素前缀)问题:stackoverflow.com/questions/21143846/…
  • @MichaelBerkowski 好吧,我确实尝试过使用命名空间。请参见代码的第 1、2、3 行。但是他们给出了一个错误。
  • 对PHP不熟悉,但是既然你把命名空间注册到$item,也许这样就可以了:$item-&gt;xpath('//amazon:OfferSummary//amazon:Amount');

标签: php xml xpath simplexml


【解决方案1】:

Amount 元素位于命名空间&lt;http://webservices.amazon.com/AWSECommerceService/2011-08-01&gt; 中。但是在您的 xpath 查询中,您根本没有将其放在任何名称空间中。所以该示例将返回 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";
}

进一步阅读


如果您不想一遍又一遍地注册命名空间前缀,您可以从 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 类,您可以加载文档,然后只需注册一次命名空间:

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

【讨论】:

  • 这并不是真正的问题。我尝试使用命名空间。请参见代码的第 1、2、3 行。 @har07 指出了这个问题。我认为在元素上设置命名空间会传播/继承给子对象,但事实并非如此,所以我必须在我使用 xpath() 的确切子对象上设置命名空间。
  • 啊!是的,您必须这样做,或者您让 SimpleXML 通过扩展它来为您做到这一点。我将添加一个示例,说明如何做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-28
  • 2010-12-27
  • 2018-03-13
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多