Xpath query results are always empty - php

I have the following XML document:
<?xml version="1.0"?>
<GetMatchingProductForIdResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetMatchingProductForIdResult Id="B0009VCOU4" IdType="ASIN" status="Success">
<Products xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
<Product>
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>ATVPDKIKX0DER</MarketplaceId>
<ASIN>B0009VCOU4</ASIN>
</MarketplaceASIN>
</Identifiers>
<AttributeSets>
<ns2:ItemAttributes xml:lang="en-US">
<ns2:Binding>Electronics</ns2:Binding>
<ns2:Brand>DOBANI</ns2:Brand>
<ns2:Feature>Handcrafted Quality, Value Priced</ns2:Feature>
<ns2:Feature>Satisfaction Guaranteed! 30-Day Return Policy!</ns2:Feature>
<ns2:ItemDimensions>
<ns2:Height Units="inches">7.00</ns2:Height>
<ns2:Length Units="inches">6.00</ns2:Length>
<ns2:Width Units="inches">6.00</ns2:Width>
</ns2:ItemDimensions>
<ns2:Label>Mid-East</ns2:Label>
<ns2:ListPrice>
<ns2:Amount>9.90</ns2:Amount>
<ns2:CurrencyCode>USD</ns2:CurrencyCode>
</ns2:ListPrice>
<ns2:Manufacturer>Mid-East</ns2:Manufacturer>
<ns2:Model>BULB</ns2:Model>
<ns2:PackageDimensions>
<ns2:Height Units="inches">3.70</ns2:Height>
<ns2:Length Units="inches">8.10</ns2:Length>
<ns2:Width Units="inches">4.00</ns2:Width>
<ns2:Weight Units="pounds">0.35</ns2:Weight>
</ns2:PackageDimensions>
<ns2:PackageQuantity>1</ns2:PackageQuantity>
<ns2:PartNumber>BULB</ns2:PartNumber>
<ns2:ProductGroup>Single Detail Page Misc</ns2:ProductGroup>
<ns2:ProductTypeName>MUSICAL_INSTRUMENTS</ns2:ProductTypeName>
<ns2:Publisher>Mid-East</ns2:Publisher>
<ns2:SmallImage>
<ns2:URL>http://ecx.images-amazon.com/images/I/31Fsu5jKWsL._SL75_.jpg</ns2:URL>
<ns2:Height Units="pixels">75</ns2:Height>
<ns2:Width Units="pixels">50</ns2:Width>
</ns2:SmallImage>
<ns2:Studio>Mid-East</ns2:Studio>
<ns2:Title>Spare Rubber Bulb</ns2:Title>
</ns2:ItemAttributes>
</AttributeSets>
<Relationships/>
<SalesRankings>
<SalesRank>
<ProductCategoryId>sdp_misc_display_on_website</ProductCategoryId>
<Rank>36468</Rank>
</SalesRank>
</SalesRankings>
</Product>
</Products>
</GetMatchingProductForIdResult>
<ResponseMetadata>
<RequestId>afnapq823haeufabq2rhalhtz</RequestId>
</ResponseMetadata>
</GetMatchingProductForIdResponse>
I am trying to get the package dimensions, but none of my xpath queries work. Have tried these using PHP's built-in xpath functions and using QueryPath 3.0.0, but neither works.
This doesn't work even though XML Tree in Chrome shows it should:
/GetMatchingProductForIdResponse/GetMatchingProductForIdResult/Products/Product/AttributeSets/ns2:ItemAttributes/ns2:PackageDimensions/ns2:Height
I thought neutralizing the namespace would get rid of the problem since the above triggers a namespace error, but it does not:
/GetMatchingProductForIdResponse/GetMatchingProductForIdResult/Products/Product/AttributeSets/*[local-name()='ItemAttributes']/*[local-name()='PackageDimensions']/*[local-name()='Height']

Xpath has no default namespace. The Xpath provided by Chrome is invalid. An Xpath element expression without a namespace prefix always matches elements without a namespace.
You need to register your own namespace prefixes and use them:
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$xpath->registerNamespace(
'p', 'http://mws.amazonservices.com/schema/Products/2011-10-01'
);
$xpath->registerNamespace(
'pd', 'http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd'
);
$result = $xpath->evaluate(
'string(
/p:GetMatchingProductForIdResponse
/p:GetMatchingProductForIdResult
/p:Products
/p:Product
/p:AttributeSets
/pd:ItemAttributes
/pd:PackageDimensions
/pd:Height
)'
);
var_dump($result);
Output: https://eval.in/150409
string(4) "3.70"

I'm afraid even the first part of your xpath is suffering for the namespace issue. Have you tried :
/*[local-name()='GetMatchingProductForIdResponse']/*[local-name()='GetMatchingProductForIdResult']/*[local-name()='Products']/*[local-name()='Product']/*[local-name()='AttributeSets']/*[local-name()='ItemAttributes']/*[local-name()='PackageDimensions']/*[local-name()='Height']
I think to use the normal xpath approach, you will need to set the corresponding namespaces on the xpath evaluator itsself.

/**
* Convert XML to JSON and return.
*/
$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
$xml = simplexml_load_string($response);
$json = json_encode($xml);
return json_decode($json,TRUE);
This worked for me

Related

SimpleXML append multiple files

I'm trying to combine multiple XML files, using SimpleXML if possible. I'm just trying to append products, children, and child data from file 2 into file 1. I'm not trying to merge elements, just append file 2 to the bottom of file 1, and so on. (Though I guess this is technically merging merchandiser elements?) The files contain the same schema and will both look similar to the example below, only thing that will be changing is the actual text. This is just XML for two different products, I added a large space in between products so that it's easier to see where it ends.
<merchandiser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="merchandiser.xsd">
<header>
<merchantId>35928</merchantId>
<merchantName>Sunspel Clothing</merchantName>
<createdOn>01/14/2016 02:03:31</createdOn>
</header>
<product product_id="14633" name="Cotton Socks" sku_number="1588/102">
<category>
<primary>Accessories</primary>
<secondary>Men's~~Socks</secondary>
</category>
<URL>
<product>
http://click.linksynergy.com/link?id=D*rqD2paIXY&offerid=191965.14633&type=15&murl=http%3A%2F%2Fwww.sunspel.com%2Fuk%2Fcotton-sock-black.html
</product>
<productImage>
http://www.sunspel.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/1/5/1588-102-new.jpg
</productImage>
</URL>
<description>
<short>
Our new cotton socks are designed by Sunspel and crafted in an Italian factory steeped in years of experience, skill and heritage. They are made from the highest quality, extra-long staple Egyptian cotton yarn which, prior to knitting is combed, twisted and mercerised to enhance the comfort, shine and absorption of the fabric as well as its resistance to pilling and shrinking.
</short>
</description>
<discount currency="GBP">
<type>amount</type>
</discount>
<price currency="GBP">
<retail>15.00</retail>
</price>
<shipping>
<availability>in-stock</availability>
</shipping>
<pixel>
http://ad.linksynergy.com/fs-bin/show?id=D*rqD2paIXY&bids=191965.14633&type=15&subid=0
</pixel>
</product>
<product product_id="15115" name="Cotton Socks" sku_number="1589/236">
<category>
<primary>Accessories</primary>
<secondary>Men's~~Socks~~Men's</secondary>
</category>
<URL>
<product>
http://click.linksynergy.com/link?id=D*rqD2paIXY&offerid=191965.15115&type=15&murl=http%3A%2F%2Fwww.sunspel.com%2Fuk%2Fmens-cotton-socks-navy-stripes.html
</product>
<productImage>
http://www.sunspel.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/1/5/1588-236-new.jpg
</productImage>
</URL>
<description>
<short>
Our new cotton socks are designed by Sunspel and crafted in an Italian factory steeped in years of experience, skill and heritage. They are made from the highest quality, extra-long staple Egyptian cotton yarn which, prior to knitting is combed, twisted and mercerised to enhance the comfort, shine and absorption of the fabric as well as its resistance to pilling and shrinking.
</short>
</description>
<discount currency="GBP">
<type>amount</type>
</discount>
<price currency="GBP">
<retail>17.00</retail>
</price>
<shipping>
<availability>in-stock</availability>
</shipping>
<pixel>
http://ad.linksynergy.com/fs-bin/show?id=D*rqD2paIXY&bids=191965.15115&type=15&subid=0
</pixel>
</product>
<product product_id="15116" name="Cotton Socks" sku_number="1589/711">
<category>
<primary>Accessories</primary>
<secondary>Men's~~Socks~~Men's</secondary>
</category>
<URL>
<product>
http://click.linksynergy.com/link?id=D*rqD2paIXY&offerid=191965.15116&type=15&murl=http%3A%2F%2Fwww.sunspel.com%2Fuk%2Fmens-cotton-socks-charcoal-melange-stripes.html
</product>
<productImage>
http://www.sunspel.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/1/5/1588-711-new.jpg
</productImage>
</URL>
<description>
<short>
Our new cotton socks are designed by Sunspel and crafted in an Italian factory steeped in years of experience, skill and heritage. They are made from the highest quality, extra-long staple Egyptian cotton yarn which, prior to knitting is combed, twisted and mercerised to enhance the comfort, shine and absorption of the fabric as well as its resistance to pilling and shrinking.
</short>
</description>
<discount currency="GBP">
<type>amount</type>
</discount>
<price currency="GBP">
<retail>17.00</retail>
</price>
<shipping>
<availability>in-stock</availability>
</shipping>
<pixel>
http://ad.linksynergy.com/fs-bin/show?id=D*rqD2paIXY&bids=191965.15116&type=15&subid=0
</pixel>
</product>
With some foreach statements I'm able to append all product children and attributes, but this doesn't actually give me the child data.
$file1 = '35928_3210820_mp.xml';
$file2 = '39153_3210820_mp.xml';
$fileout = 'ukmerge.xml';
$xml1 = simplexml_load_file( $file1 );
$xml2 = simplexml_load_file( $file2 ); // loop through the product and add them and their attributes to xml1
$product = $xml2->product;
$prod = $xml2->merchandiser->header->product;
$category = $product->category;
$url = $product->URL;
$description = $product->description;
foreach( $xml2->children() as $child ) {
$new = $xml1->addChild( $child->getName() , htmlspecialchars($child) );
foreach( $child->attributes() as $key => $value ) {
$new->addAttribute( $key, $value );
}
} $fh = fopen( $fileout, 'w') or die ( "can't open file $fileout" );
fwrite( $fh, $xml1->asXML() );
fclose( $fh );
When I try to add on from there then everything gets messed up and nothing is in the correct place/order anymore. I'd also like to put this into a function since I'm going to be doing it often. Any help is greatly appreciate as I've been struggling with this for a few days now and have scowered over a few dozen stackoverflow and php.net threads.
One of the things that's confusing me is the <merchandiser> and <header> tags that every file starts with. Once the merchandiser tag ends it is the end of the document so I need to take only what's inside the merchandiser tag of file 2 and append it inside the merchandiser tag of file 1. The header tag just confuses me cause I'm not sure if it's gets in the way or not.
As preliminary note, your XML sample is malformed. Also it is not coherent with your code (i.e. there is not ->merchandiser->header->product ).
So, in this example I will use a different sample, like this one (file1.xml):
<root>
<product>
<name>Product 1</name>
</product>
<product>
<name>Product 2</name>
</product>
</root>
and this one (file2.xml):
<root>
<product>
<name>Product 3</name>
</product>
<product>
<name>Product 4</name>
</product>
</root>
You don't want to use DOMDocument->importNode() due “it kept throwing a lot of errors”.
You can use DOMDocument in conjunction with SimpleXML and dom_import_simplexml() function.
First of all, prepare destination XML: load the file with SimpleXML, create a DOMDocument using dom_import_simplexml() and set $parent variable to <root> element:
$dst = simplexml_load_file( 'file1.xml' );
$dst = dom_import_simplexml( $dst )->ownerDocument;
$parent = $dst->getElementsByTagName( 'root' )->item(0);
Then, load second file with SimpleXML:
$src = simplexml_load_file( 'file2.xml' );
Through a foreach() loop, import each <product> element from SimpleXML to DOMDocument and appent it as child of $parent node:
foreach( $src->product as $product )
{
$node = dom_import_simplexml( $product );
$node = $dst->importNode( $node, 1 );
$parent->appendChild( $node );
}
Now, your merged XML is ready. You can print it using $dst->saveXML().
I've not be able to product a correctly indented XML. BTW to do this, you can reload-it:
$final = new DOMDocument();
$final->loadXML( $dst->saveXML(), LIBXML_NOBLANKS );
$final->formatOutput = True;
echo $final->saveXML();
Final output:
<?xml version="1.0"?>
<root>
<product>
<name>Product 1</name>
</product>
<product>
<name>Product 2</name>
</product>
<product>
<name>Product 3</name>
</product>
<product>
<name>Product 4</name>
</product>
</root>

How to select a single ID among many returned by Amazon API?

I am working with the Amazon API, and they send be the response in XML format. I am trying to sort through the data, but since there are 15 different tags that I am looking for, I am not exactly sure how to go about this. I know that there is dom->getElementByTagName(''). I am wondering how I would go about finding all 15 results. The response is as follows:
<GetMatchingProductForIdResult Id="082686322638" IdType="UPC" status="Success">
<ns2:ListPrice>
<ns2:Amount>9.99</ns2:Amount>
<ns2:CurrencyCode>USD</ns2:CurrencyCode>
</ns2:ListPrice>
<SalesRankings>
<SalesRank>
<ProductCategoryId>toy_display_on_website</ProductCategoryId>
<Rank>4150</Rank>
</SalesRank>
<SalesRank>
<ProductCategoryId>2229575011</ProductCategoryId>
<Rank>8</Rank>
</SalesRank>
</SalesRankings>
</GetMatchingProductForIdResult>
This is repeated 3 times.
I am looking for:
The Id value in <GetMatchingProductForIdResult Id="082686322638" IdType="UPC" status="Success">
<ns2:Amount></ns2:Amount>
<SalesRank>
<ProductCategoryId>toy_display_on_website</ProductCategoryId>
<Rank>4150</Rank>
</SalesRank>
How do I add all 3 tags to my code?
So far, I have this:
foreach($xml->children() as $items) {
echo $items->GetMatchingProductForIdResult['Id'] . ',';
echo $items->Product->SalesRankings->SalesRank->Rank . ", ";
echo $items->Products->Product->ns2:Amount . ", "; }
Assuming that you specifically want the Id of the GetMatchingProductForIdResult with IdType equal to "UPC",
$dom = new DOMDocument();
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
$id = $xpath->query("//GetMatchingProductForIdResult[#IdType='UPC']/#Id");
then $id will be 082686322638 as requested.

PHP xpath get elements by attribute in foreach loop

im am trying to loop through all the LINE_ITEMS and it works fine but in the foreach-loop im trying to access the single LINE_ITEM by attribute by using xpath but i don't get any result. Does anyone know the problem ?
foreach($items = $xmlData->xpath('//zw:LINE_ITEM') as $item) {
$item->xpath('//namespace:PID[#type="erp_pid"]'); No result
}
$xmlData->xpath('//zw:LINE_ITEM')
works fine i get all LINE_ITEMS but
when i try to do some xpath on the item i don't get any result.
How can i access the PID value of for example "erp_pid" ?
<?xml version="1.0" encoding="UTF-8"?>
<NM_DOCS>
<NM_DOC>
<DOCUMENT qualifier="default" role="original" test="false" type="orders">
<VERSION>4.0</VERSION>
<HEADER>
<CONTROL_INFO>
<LAST_SAVE_DATE>2015-03-18T13:44:32+01:00</LAST_SAVE_DATE>
<PROCESS_TYPE>silent</PROCESS_TYPE>
<SOURCE>sales</SOURCE>
</CONTROL_INFO>
<SOURCING_INFO>
<REFERENCES>
<REFERENCE type="order_nexmart">
<ID>109546063</ID>
<DATES>
<DATE type="order">2015-03-18T13:44:30+01:00</DATE>
</DATES>
</REFERENCE>
</REFERENCES>
</SOURCING_INFO>
<DOCUMENT_INFO>
<DOCUMENT_ID>Test bestellnummer</DOCUMENT_ID>
<DATES>
<DATE type="delivery_ordered">2015-04-19T12:41:41+02:00</DATE>
</DATES>
<PARTIES>
<PARTY type="buyer">
<BUSINESS_ROLE>commercial</BUSINESS_ROLE>
<PORTAL_ID>BDE600028</PORTAL_ID>
<ADDITIONAL_IDS>
</ADDITIONAL_IDS>
<ADDRESS>
</ADDRESS>
<CONTACT_DETAILS>
<ACCOUNTS>
<ID type="emart">sales.nexmart</ID>
</ACCOUNTS>
</CONTACT_DETAILS>
</PARTY>
<PARTY type="supplier">
<BUSINESS_ROLE>commercial</BUSINESS_ROLE>
<PORTAL_ID>zweygart_app_de</PORTAL_ID>
<ADDITIONAL_IDS>
</ADDITIONAL_IDS>
<ADDRESS>
</ADDRESS>
<CONTACT_DETAILS>
</CONTACT_DETAILS>
</PARTY>
<PARTY type="delivery">
<BUSINESS_ROLE>commercial</BUSINESS_ROLE>
<ADDRESS>
</ADDRESS>
<CONTACT_DETAILS>
</CONTACT_DETAILS>
</PARTY>
<PARTY type="invoice_recipient">
<ADDRESS>
</ADDRESS>
<CONTACT_DETAILS>
</CONTACT_DETAILS>
</PARTY>
</PARTIES>
<REMARKS>
<REMARK type="order">Test bemerkung</REMARK>
</REMARKS>
</DOCUMENT_INFO>
</HEADER>
<LINE_ITEMS>
<LINE_ITEM>
<PRODUCT_ID>
<SUPPLIER_PID>119556</SUPPLIER_PID>
<GTIN>4030646269130</GTIN>
<ADDITIONAL_PIDS>
<PID type="supplier_pid_original">119556</PID>
<PID type="gtin_original">4030646269130</PID>
<PID type="erp_pid">119556</PID>
</ADDITIONAL_PIDS>
<DESCRIPTIONS>
<DESCR type="short">short desc</DESCR>
<DESCR type="short_original">some text</DESCR>
</DESCRIPTIONS>
</PRODUCT_ID>
</LINE_ITEM>
<LINE_ITEM>
<PRODUCT_ID>
<SUPPLIER_PID>123456789</SUPPLIER_PID>
<GTIN>123456789</GTIN>
<ADDITIONAL_PIDS>
<PID type="supplier_pid_original">123456</PID>
<PID type="gtin_original">123456</PID>
<PID type="erp_pid">123456</PID>
</ADDITIONAL_PIDS>
<DESCRIPTIONS>
<DESCR type="short">short desc</DESCR>
<DESCR type="short_original">Some description</DESCR>
</DESCRIPTIONS>
</PRODUCT_ID>
</LINE_ITEM>
</LINE_ITEMS>
</DOCUMENT>
</NM_DOC>
</NM_DOCS>
The problem is not XPath but SimpleXML. SimpleXMLElement::xpath() is limited. It converts the result into an array of SimpleXMLElement objects, but here are other nodes in a DOM. More important you will have to register the namespaces on each new SimpleXMLElement again.
$element = new SimpleXMLElement($xml);
$element->registerXPathNamespace('namespace', 'urn:foo');
foreach($element->xpath('//namespace:LINE_ITEMS/namespace:LINE_ITEM') as $item) {
$item->registerXPathNamespace('namespace', 'urn:foo');
var_dump((string)$item->xpath('.//namespace:PID[#type="erp_pid"]')[0]);
}
Output:
string(6) "119556"
string(6) "123456"
You might notice that I prefixed your detail expression with an .. A slash at the start of the expression always makes it relative to the document itself, not the current node. The . represents the current node.
If you use DOM directly, you create a separate DOMXPath object and register the namespaces on this object. Additionally you can use XPath expressions that return scalar values.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('namespace', 'urn:foo');
foreach($xpath->evaluate('//namespace:LINE_ITEMS/namespace:LINE_ITEM') as $node) {
var_dump($xpath->evaluate('string(.//namespace:PID[#type="erp_pid"])', $node));
}
This works for me:
foreach($xmlData->LINE_ITEM as $item) {
$erp = ( $item->xpath('//PID[#type="erp_pid"]'));
foreach($erp as $v) {
echo $v. " / ";
}
}
Just remove the namespace in your xpath, your xml doesn't use a namespace.
If you want to iterate through a part of the xml be sure to use the correct path.

Parsing Amazon MWS Scratchpad response

I am trying to parse xml file from amazon but found difficulties. I am using simplexml_load_string
$xml = simplexml_load_string( 'My xml here' );
but when I do
echo $xml->GetMatchingProductResult->Product->AttributeSets;
it shows nothing
How can I access the Brand value?
My xml file:
<?xml version="1.0"?>
<GetMatchingProductResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetMatchingProductResult ASIN="B003IOSNNQ" status="Success">
<Product xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>A1F83G8C2ARO7P</MarketplaceId>
<ASIN>B003IOSNNQ</ASIN>
</MarketplaceASIN>
</Identifiers>
<AttributeSets>
<ns2:ItemAttributes xml:lang="en-GB">
<ns2:Binding>Misc.</ns2:Binding>
<ns2:Brand>eSecure</ns2:Brand>
<ns2:Feature>Colour: Classic Black</ns2:Feature>
<ns2:Feature>Thickness: Approximately 40mm - More than msot sellers</ns2:Feature>
<ns2:Feature>Dimensions [length x width at widest and narrowest]: approximately 280 x 190 x 70mm</ns2:Feature>
<ns2:Feature>Material: Stretch Lycra, Silica Gel</ns2:Feature>
<ns2:Feature>Ideal for Standard Bicycle / Static Exercise Bike</ns2:Feature>
<ns2:Label>eSecure</ns2:Label>
<ns2:Manufacturer>eSecure</ns2:Manufacturer>
<ns2:PackageDimensions>
<ns2:Height Units="inches">2.13</ns2:Height>
<ns2:Length Units="inches">10.71</ns2:Length>
<ns2:Width Units="inches">7.72</ns2:Width>
<ns2:Weight Units="pounds">0.49</ns2:Weight>
</ns2:PackageDimensions>
<ns2:PackageQuantity>1</ns2:PackageQuantity>
<ns2:ProductGroup>Sports</ns2:ProductGroup>
<ns2:ProductTypeName>SPORTING_GOODS</ns2:ProductTypeName>
<ns2:Publisher>eSecure</ns2:Publisher>
<ns2:SmallImage>
<ns2:URL>http://ecx.images-amazon.com/images/I/41OIjmpza2L._SL75_.jpg</ns2:URL>
<ns2:Height Units="pixels">75</ns2:Height>
<ns2:Width Units="pixels">75</ns2:Width>
</ns2:SmallImage>
<ns2:Studio>eSecure</ns2:Studio>
<ns2:Title>eSecure - Extra Comfort Bike Bicycle Gel Saddle Seat Cover</ns2:Title>
</ns2:ItemAttributes>
</AttributeSets>
<Relationships/>
<SalesRankings>
<SalesRank>
<ProductCategoryId>sports_display_on_website</ProductCategoryId>
<Rank>398</Rank>
</SalesRank>
<SalesRank>
<ProductCategoryId>458338031</ProductCategoryId>
<Rank>1</Rank>
</SalesRank>
</SalesRankings>
</Product>
</GetMatchingProductResult>
<ResponseMetadata>
<RequestId>9b44aba6-d1fa-486a-8114-2bc4f9311d8d</RequestId>
</ResponseMetadata>
</GetMatchingProductResponse>
I know this is an old question but I had a hard time with the same thing.
First simplexml_load_string doesn't handle the namespaces thrown in like this, so you could go recursively and check and then load the namespaces, evaluate the xml and waste a lot of time ...
In my case I didn't care about evaluating the format of the xml with the namespaces, so I just removed them from the string with regex. After this simplexml_load_string won't omit those parts anymore and it's easy to get an array out of it with json_encode and json_decode.
//convert an xml string into an array
function xml2array($xml){
$xml = preg_replace('/(<\/?)\w+:([^>]*>)/', '$1$2', $xml); //get rid of namespaces
$xml = simplexml_load_string($xml);
return json_decode(json_encode($xml),true); //use built-in stuff
}
The only problem with this is when the xml is full of attributes containing useful data, then you would need something else instead of simplexml_load_string. Note that it returns an #attributes element from this:
<GetMatchingProductResult ASIN="B003IOSNNQ" status="Success">
but omits it from this part:
<Width Units="pixels">75</Width>
I have no idea why or how deep down it would check in the nested tags.
echo $xmlobj->children('',true)->ListMatchingProductsResult->Products->Product->AttributeSets->children('ns2',true)->ItemAttributes->Brand.PHP_EOL;

Selecting / Filtering XML Element with XPath

here is my amazon mws api responce
<?xml version="1.0"?>
<GetMyPriceForSKUResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetMyPriceForSKUResult SellerSKU="ds-tru-6sss" status="Success">
<Product xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>Assssssss</MarketplaceId>
<ASIN>sss</ASIN>
</MarketplaceASIN>
<SKUIdentifier>
<MarketplaceId>Afasrfd</MarketplaceId>
<SellerId>ssssss</SellerId>
<SellerSKU>dssss</SellerSKU>
</SKUIdentifier>
</Identifiers>
<Offers>
<Offer>
<BuyingPrice>
<LandedPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>12.49</Amount>
</LandedPrice>
<ListingPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>12.49</Amount>
</ListingPrice>
<Shipping>
<CurrencyCode>USD</CurrencyCode>
<Amount>0.00</Amount>
</Shipping>
</BuyingPrice>
<RegularPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>12.49</Amount>
</RegularPrice>
<FulfillmentChannel>MERCHANT</FulfillmentChannel>
<ItemCondition>New</ItemCondition>
<ItemSubCondition>New</ItemSubCondition>
<SellerId>Aadada</SellerId>
<SellerSKU>ssss</SellerSKU>
</Offer>
<Offer>
<BuyingPrice>
<LandedPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>1000.00</Amount>
</LandedPrice>
<ListingPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>1000.00</Amount>
</ListingPrice>
<Shipping>
<CurrencyCode>USD</CurrencyCode>
<Amount>0.00</Amount>
</Shipping>
</BuyingPrice>
<RegularPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>1000.00</Amount>
</RegularPrice>
<FulfillmentChannel>MERCHANT</FulfillmentChannel>
<ItemCondition>New</ItemCondition>
<ItemSubCondition>New</ItemSubCondition>
<SellerId>ssss</SellerId>
<SellerSKU>sss</SellerSKU>
</Offer>
</Offers>
</Product>
</GetMyPriceForSKUResult>
<ResponseMetadata>
<RequestId>e0ef1c2c-4f35-4316-8629-faadadd</RequestId>
</ResponseMetadata>
</GetMyPriceForSKUResponse>
and to select amount (12.49) from
<ListingPrice>
<CurrencyCode>USD</CurrencyCode>
<Amount>12.49</Amount>
</ListingPrice>
I am trying ,
// from curl
$result = curl_exec ($ch);
$xmldoc = new DOMDocument();
$xmldoc->load($result);
$xpathvar = new Domxpath($xmldoc);
$queryResult = $xpathvar->query('/Amount');
foreach($queryResult as $result){
echo $result;
}
I am expecting more then one value for this, but I am getting none at all.
Sorry, I am not good at XPath, can somebody guide me?
Currently I found errors in your code:
First: Use two // to select an element regardless of where it is located in the xml tree.
$queryResult = $xpathvar->query('//Amount');
Second: thanks #Ranon. You'll take care of the documents xml namespace:
// Register Namespace mws
$xpathvar->registerNamespace("mws", 'http://mws.amazonservices.com/schema/Products/2011-10-01');
... and use it, means:
$queryResult = $xpathvar->query('//mws:Amount');
Third: If you want to select the text node (between the <amount> nodes) you should use:
$queryResult = $xpathvar->query('//mws:Amount/text()');
Otherwise you can select the parent element <Amount> (as you already doing) and retrieve the value with PHP. Then you have to change your code to:
$queryResult = $xpathvar->query('//mws:Amount');
foreach($queryResult as $result){
echo $result->nodeValue; // echo the node value, not the node 'itself'
}
Fourth: Also note another error in your code. When you create a DOMDocument from an xml string you'll have to use:
$document->loadXML($result);
Fifth: You told that you want to retrieve the <Amount> elements form inside <ListingPrice> elements. Note that there are also <Amount> elements inside <RegularPrice> elements. So it does matter where the <Amount> element is located in tree. Use the following query to obtain only listing price amounts:
$queryResult = $xpathvar->query('//mws:ListingPrice/mws:Amount');
Amazon returns XML using a namespace which you have to declare and use.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// from curl
$result = curl_exec($ch);
$xmldoc = new DOMDocument();
$xmldoc->loadXML($result);
$xpathvar = new Domxpath($xmldoc);
// Register Namespace mws
$xpathvar->registerNamespace("mws", 'http://mws.amazonservices.com/schema/Products/2011-10-01');
// Query using namespace mws
$queryResult = $xpathvar->query('//mws:Amount');
foreach($queryResult as $result){
echo $result->nodeValue;
}
I selected the namespace identifier mws arbitrarily from the subdomain, you can choose another if you want.
I corrected some other errors in the code found by #hek2mgl.
The XPath expression is wrong. You need '//Amount' to select all the "Amount" elements

Categories