问题
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