Why doesn't this PHP SimpleXML XPath find the values?

别来无恙 提交于 2020-01-30 06:42:08

问题


I'm expecting an array of Amount, but for some reason it refused to be found. I tried many combinations of setting the name space, but it gives the error 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">

回答1:


The Amount element is in the namespace <http://webservices.amazon.com/AWSECommerceService/2011-08-01>. But in your xpath query you put it in no namespace at all. So the example will return 0 elements:

$xml = simplexml_load_string($buffer);

$result = $xml->xpath('//Amount');
printf("Result (%d):\n", count($result));
foreach ($result as $node) {
    echo $node->asXML(), "\n";
}

The output is rather short:

Result (0):

Instead you need to tell that you want the Amount element in that specific namespace. You do that by registering a namespace-prefix and use it in your xpath query:

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

Now the output is with all those elements:

Result (24):
<Amount>217</Amount>
<Amount>1</Amount>
<Amount>800</Amount>
<Amount>1</Amount>
<Amount>498</Amount>
...

Take care that if you've got more than one element-name to check for in your xpath query, that you need to tell the correct namespace for each of them. Example:

$result = $xml->xpath('/*/amazon:Items/amazon:Item//amazon:Amount');

You can find the example code also online here: https://eval.in/314347 (backup)- otherwise the examples from my answer minus the XML at a glance:

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

Further Readings

  • Parse XML with Namespace using SimpleXML
  • XPath in SimpleXML for default namespaces without needing prefixes

If you don't want to register the namespace prefixes over and over again, you can extend from SimpleXMLElement and manage that registration list your own:

/**
 * 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);
    }
}

With this MyXml class you can load the document and then you would only need to register the namespace once:

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


来源:https://stackoverflow.com/questions/29664051/why-doesnt-this-php-simplexml-xpath-find-the-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!