1

How can I extract the values from the Amazon MWS XML file shown below?

Sadly I'm not very good with PHP (or XML). I have been reading/trying everything I can find on all sites I can find but I think I'm just not up to the job.

I've tried DOM, simpleXML, namespaces etc etc but must have simply got it all wrong.

I've tried this snippet of code:

    $response = $service->GetMyPriceForASIN($request);

    $dom = new DOMDocument();
    $dom->loadXML($response->toXML());
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;
    $dom->saveXML();     

    $x = $dom->documentElement;
    foreach ($x->childNodes AS $item) {
    print $item->nodeName . " = " . $item->nodeValue . "<br>";
    }

However it only returns this data from the outermost nodes, shown here:

GetMyPriceForASINResult = xxxxxxxxxxxB07DNYLZP5USD9.97USD9.97USD0.00USD15.99AMAZONNewNewxxxxxxxxxxxxxDazzle Tattoos ResponseMetadata = xxxxxxxxxxxxxxxxxxxxxxxx

What I'm simply trying to get are the currency and prices that are the children under the [Offers] node.

<?xml version="1.0"?>
<GetMyPriceForASINResponse 
xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
  <GetMyPriceForASINResult ASIN="B07DNYLZP5" status="Success">
    <Product>
      <Identifiers>
        <MarketplaceASIN>
          <MarketplaceId>xxxxxxxxxxxxxxxxxx</MarketplaceId>
          <ASIN>B07DNYLZP5</ASIN>
        </MarketplaceASIN>
      </Identifiers>
      <Offers>
        <Offer>
          <BuyingPrice>
            <LandedPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </LandedPrice>
            <ListingPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </ListingPrice>
            <Shipping>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>0.00</Amount>
            </Shipping>
          </BuyingPrice>
          <RegularPrice>
            <CurrencyCode>USD</CurrencyCode>
            <Amount>15.99</Amount>
          </RegularPrice>
          <FulfillmentChannel>AMAZON</FulfillmentChannel>
          <ItemCondition>New</ItemCondition>
          <ItemSubCondition>New</ItemSubCondition>
          <SellerId>xxxxxxxxxx</SellerId>
          <SellerSKU>Dazzle Tattoos</SellerSKU>
        </Offer>
      </Offers>
    </Product>
  </GetMyPriceForASINResult>
  <ResponseMetadata>
    <RequestId>xxxxxxxxxxxxxxxxxxxxx</RequestId>
  </ResponseMetadata>
</GetMyPriceForASINResponse>

Update

The link provided by Nigel doesn't help - it is basically a compendium of information.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • you have to check that if $x->nodeName=='CurrencyCode' or Price. – Kaushik Andani Aug 23 '18 at 15:27
  • Possible duplicate of [How do you parse and process HTML/XML in PHP?](https://stackoverflow.com/questions/3577641/how-do-you-parse-and-process-html-xml-in-php) – Nigel Ren Aug 23 '18 at 15:46
  • Hi Kaushik - yes but the problem is it only finds the outer nodes so I can't check th others - it doesn't get that far down the tree. – TheTerribleProgrammer Aug 23 '18 at 16:11
  • Hi Nigel - yes it is a possible duplicate - however I've read everything and am still stuck so I'm hoping someone will help. – TheTerribleProgrammer Aug 23 '18 at 16:12
  • Read a little bit about Xpath, you can use it with [DOMXpath::evaluate()](http://php.net/manual/en/domxpath.evaluate.php). It allows you to use expressions to fetch nodes/values from a DOM. – ThW Aug 23 '18 at 19:23

1 Answers1

1

XML in general is fairly easy to work with, but you've stumbled over one of the things that makes it much tricker: XML namespaces. This is given away by the presence of the xmlns attribute in the outer element.

My first thought upon looking at your document was, and since I am not familiar with this API, "offers" sounds like it could be plural. I therefore modified the test document to allow for that, since <Offers> is probably designed to contain zero, one, or more than one <Offer>.

So, for testing purposes, that would look like this (I've just duplicated the single <Offer> for convenience):

<?xml version="1.0"?>
<GetMyPriceForASINResponse 
    xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01"
>
  <GetMyPriceForASINResult ASIN="B07DNYLZP5" status="Success">
    <Product>
      <Identifiers>
        <MarketplaceASIN>
          <MarketplaceId>xxxxxxxxxxxxxxxxxx</MarketplaceId>
          <ASIN>B07DNYLZP5</ASIN>
        </MarketplaceASIN>
      </Identifiers>
      <Offers>
        <Offer>
          <BuyingPrice>
            <LandedPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </LandedPrice>
            <ListingPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </ListingPrice>
            <Shipping>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>0.00</Amount>
            </Shipping>
          </BuyingPrice>
          <RegularPrice>
            <CurrencyCode>USD</CurrencyCode>
            <Amount>15.99</Amount>
          </RegularPrice>
          <FulfillmentChannel>AMAZON</FulfillmentChannel>
          <ItemCondition>New</ItemCondition>
          <ItemSubCondition>New</ItemSubCondition>
          <SellerId>xxxxxxxxxx</SellerId>
          <SellerSKU>Dazzle Tattoos</SellerSKU>
        </Offer>
        <Offer>
          <BuyingPrice>
            <LandedPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </LandedPrice>
            <ListingPrice>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>9.97</Amount>
            </ListingPrice>
            <Shipping>
              <CurrencyCode>USD</CurrencyCode>
              <Amount>0.00</Amount>
            </Shipping>
          </BuyingPrice>
          <RegularPrice>
            <CurrencyCode>USD</CurrencyCode>
            <Amount>15.99</Amount>
          </RegularPrice>
          <FulfillmentChannel>AMAZON</FulfillmentChannel>
          <ItemCondition>New</ItemCondition>
          <ItemSubCondition>New</ItemSubCondition>
          <SellerId>xxxxxxxxxx</SellerId>
          <SellerSKU>Dazzle Tattoos</SellerSKU>
        </Offer>
      </Offers>
    </Product>
  </GetMyPriceForASINResult>
  <ResponseMetadata>
    <RequestId>xxxxxxxxxxxxxxxxxxxxx</RequestId>
  </ResponseMetadata>
</GetMyPriceForASINResponse>

The PHP code I used is as follows. I've used SimpleXML here, as I am familiar with it, but I expect there would be a DOMDocument solution as well.

<?php

$file = 'prices.xml';
$doc = simplexml_load_file($file);

// Examine the namespaces
$namespaces = $doc->getNamespaces(true);    
print_r($namespaces);

$nsDoc = $doc->children($namespaces['']);
$nsDoc->registerXPathNamespace('p', 'http://mws.amazonservices.com/schema/Products/2011-10-01');

$nodes = $nsDoc->xpath('//p:GetMyPriceForASINResult/p:Product/p:Offers/p:Offer');
foreach ($nodes as $node)
{
    print_r($node);
}

My first print_r will show you the namespaces you have in your document, and the only one you have is the null namespace. Since this is empty, your tags are not prefixed with a string (e.g. Amazon:Product) but the namespace nevertheless still exists. That looks like this:

Array
(
    [] => http://mws.amazonservices.com/schema/Products/2011-10-01
)

I then get a namespaced version of the document ($doc->children($namespaces[''])) to work with. I have also declared the name of the namespace to be p (using registerXPathNamespace) which is an arbitrary name - you can use anything here that makes sense to you. (I did try registering a null name ('') but this did not work).

Finally, I use XPath as suggested in the comments, but since the namespace applies to the parent and all its children, I have added my namespace alias in front of every tag. (XPath is basically a query language for XML, and an explanation of it requires a book or a manual, so I will refrain from digging in too deeply here. If you are going to be doing more than a cursory bit of XML, I recommend finding an XML test application on the web and trying out some queries).

The output produces the two (identical) <Offer> nodes, which are thus:

SimpleXMLElement Object
(
    [BuyingPrice] => SimpleXMLElement Object
        (
            [LandedPrice] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 9.97
                )

            [ListingPrice] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 9.97
                )

            [Shipping] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 0.00
                )

        )

    [RegularPrice] => SimpleXMLElement Object
        (
            [CurrencyCode] => USD
            [Amount] => 15.99
        )

    [FulfillmentChannel] => AMAZON
    [ItemCondition] => New
    [ItemSubCondition] => New
    [SellerId] => xxxxxxxxxx
    [SellerSKU] => Dazzle Tattoos
)
SimpleXMLElement Object
(
    [BuyingPrice] => SimpleXMLElement Object
        (
            [LandedPrice] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 9.97
                )

            [ListingPrice] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 9.97
                )

            [Shipping] => SimpleXMLElement Object
                (
                    [CurrencyCode] => USD
                    [Amount] => 0.00
                )

        )

    [RegularPrice] => SimpleXMLElement Object
        (
            [CurrencyCode] => USD
            [Amount] => 15.99
        )

    [FulfillmentChannel] => AMAZON
    [ItemCondition] => New
    [ItemSubCondition] => New
    [SellerId] => xxxxxxxxxx
    [SellerSKU] => Dazzle Tattoos
)

You will want to dereference items within each node too. To do that, you can use object references and then casting to the type you want. For example:

$fulfillment = (string) $node->FulfillmentChannel;
echo "$fulfillment\n";

In the loop, this will produce the values we expect:

AMAZON
AMAZON
halfer
  • 19,824
  • 17
  • 99
  • 186
  • Thank you thank you thank you!!! This has helped me so much. I appreciate you taking the time to do this for me. – TheTerribleProgrammer Aug 24 '18 at 17:32
  • No worries @TheTerribleProgrammer, you are welcome. If you are keen on getting some practice, you could convert it to `DOMDocument` now you know what the issue is. This might be a good idea also if you have used `DOMDocument` elsewhere in your code - in general, you don't want to use more than one library to do the same thing. However, if you just want to get it working, use this one. `:-)` – halfer Aug 24 '18 at 17:34
  • (I fought with a financial API a couple of years back, and they just _love_ their XML namespaces!) – halfer Aug 24 '18 at 17:35