Reputation: 23
I have created a project to read the Amazon Product Advertising API, retrieving an XML document using the following code:
WebRequest request = HttpWebRequest.Create(signedurl);
WebResponse responseStream = request.GetResponse();
XmlDocument doc = new XmlDocument();
doc.Load(responseStream.GetResponseStream());
And, through further research I have been able to access element values using the following;
XmlNode Item_IdNode = doc.GetElementsByTagName("ASIN").Item(0);
XmlNode PriceNode = doc.GetElementsByTagName("FormattedPrice").Item(3);
string F_Price = PriceNode.InnerText;
string xml_ItemId = Item_IdNode.InnerText;
This code work fine for accessing a product ASIN and price for a single record, but I would like to retrieve up to 10 records per request.
So far, I know I can increment the "Item(0)" to page through the other item ASIN's, but the "FormattedPrice" element is repeated many times per product, and may not necessarily appear at point 6,9,12 for the other product records.
For each 10 records retrieved, I already know (use) the ASIN (unique product reference) in the api call to select the specific records.
Where I would like to progress the code to is to "search" the XML response for item ASIN "12345", then drill down to the specific node "OfferSummary/LowestNewPrice/FormattedPrice" to retrieve the item price into a variable, and so on for all other ASIN's item prices.
Here is an extract of the first two XML item records (if this helps).
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<Item><ASIN>12345</ASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1098</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£10.98</FormattedPrice>
</LowestNewPrice>
</OfferSummary>
.
.
.
.
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<Item><ASIN>23456</ASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1098</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£10.98</FormattedPrice>
</LowestNewPrice>
</OfferSummary>
.
.
.
.
I have some asp.net experience, but have not used XML 'readers' before and would be grateful for any pointers in the right direction on 'searching' the XML file for each ASIN, and its corresponding 'FormattedPrice'.
Hope this is sufficient information, please let me know if any further information is needed.
Many thanks, James
EDITED UPDATE: 11 Jan 2016
Many thanks for everyone's replies, I have been working to incorporate your responses into a working sample in my code (for the last week, trying to figure it out for myself). Although after many days for trying I still haven't got a final working solution.
I have adapted my 'query' as follows:
var res = XElement.Load(Server.MapPath("/App_Data/AWSS.xml"))
.Descendants("ASIN").FirstOrDefault(elem => elem.Value == "B001MS70F2")
.Parent.Descendants("FormattedPrice").Select(elem => elem.Value)
.FirstOrDefault();
Response.Write(res);
Which works up to a point, but in my initial post I had only posted an extract from the full XML data. The above code works for the sample XML I provided, but not for the full data which includes what I believe is the 'root'. If I delete the 'ItemLookupResponse' node (and request decedents), the code works, but not for the full XML data as below;
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<HTTPHeaders>
<Header Name="UserAgent" Value="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36"/>
</HTTPHeaders>
<RequestId>#############</RequestId>
<Arguments>
<Argument Name="AWSAccessKeyId" Value="#############"/>
<Argument Name="AssociateTag" Value="#############"/>
<Argument Name="IdType" Value="ASIN"/>
<Argument Name="ItemId" Value="B001MS70F2,B007W1RSZA"/>
<Argument Name="Operation" Value="ItemLookup"/>
<Argument Name="ResponseGroup" Value="Offers"/>
<Argument Name="Service" Value="AWSECommerceService"/>
<Argument Name="Timestamp" Value="2016-01-06T23:01:02Z"/>
<Argument Name="Signature" Value="#############"/>
</Arguments>
<RequestProcessingTime>0.0209750000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<IdType>ASIN</IdType>
<ItemId>B001MS70F2</ItemId>
<ItemId>B007W1RSZA</ItemId>
<ResponseGroup>Offers</ResponseGroup>
<VariationPage>All</VariationPage>
</ItemLookupRequest>
</Request>
<Item>
<ASIN>B001MS70F2</ASIN>
<ParentASIN>B019IGAHUY</ParentASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1049</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£10.49</FormattedPrice>
</LowestNewPrice>
<TotalNew>32</TotalNew>
<TotalUsed>0</TotalUsed>
<TotalCollectible>0</TotalCollectible>
<TotalRefurbished>0</TotalRefurbished>
</OfferSummary>
<Offers>
<TotalOffers>1</TotalOffers>
<TotalOfferPages>1</TotalOfferPages>
<MoreOffersUrl>
http://www.amazon.co.uk/gp/offer-listing/B001MS70F2%3FSubscriptionId%3DAKIAI4V5X2Q7F3BOU7MA%26tag%3Dbusin02-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB001MS70F2
</MoreOffersUrl>
<Offer>
<OfferAttributes>
<Condition>New</Condition>
</OfferAttributes>
<OfferListing>
<OfferListingId>
VbyiDUr1A7VXNun65VvEF8WmWG3ZOzirk%2BGjIdOOGBB38lLlcRYaEKyl4pS6hdrqhZuOLqfW4uTVLtqsCUfanWyEaltghotq
</OfferListingId>
<Price>
<Amount>1049</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£10.49</FormattedPrice>
</Price>
<AmountSaved>
<Amount>284</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£2.84</FormattedPrice>
</AmountSaved>
<PercentageSaved>21</PercentageSaved>
<Availability>Usually dispatched within 24 hours</Availability>
<AvailabilityAttributes>
<AvailabilityType>now</AvailabilityType>
<MinimumHours>0</MinimumHours>
<MaximumHours>0</MaximumHours>
</AvailabilityAttributes>
<IsEligibleForSuperSaverShipping>1</IsEligibleForSuperSaverShipping>
<IsEligibleForPrime>1</IsEligibleForPrime>
</OfferListing>
</Offer>
</Offers>
</Item>
<Item>
<ASIN>B007W1RSZA</ASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1630</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£16.30</FormattedPrice>
</LowestNewPrice>
<TotalNew>7</TotalNew>
<TotalUsed>0</TotalUsed>
<TotalCollectible>0</TotalCollectible>
<TotalRefurbished>0</TotalRefurbished>
</OfferSummary>
<Offers>
<TotalOffers>1</TotalOffers>
<TotalOfferPages>1</TotalOfferPages>
<MoreOffersUrl>
http://www.amazon.co.uk/gp/offer-listing/B007W1RSZA%3FSubscriptionId%3DAKIAI4V5X2Q7F3BOU7MA%26tag%3Dbusin02-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB007W1RSZA
</MoreOffersUrl>
<Offer>
<OfferAttributes>
<Condition>New</Condition>
</OfferAttributes>
<OfferListing>
<OfferListingId>
VbyiDUr1A7VXNun65VvEF74FLb5xO3BqKAr7e2DBjtDJNt3ZXQTDuPuGzaLdifzl2xEs1x4swNRE3U6yP3JfXvjWXxBDUM1vGxR8eaR1suappuRh5ZARKbjoGHx3NvEpVdrLfxvwmIzXoFSSq50uWg%3D%3D
</OfferListingId>
<Price>
<Amount>1630</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£16.30</FormattedPrice>
</Price>
<Availability>Usually dispatched within 1-2 business days</Availability>
<AvailabilityAttributes>
<AvailabilityType>now</AvailabilityType>
<MinimumHours>24</MinimumHours>
<MaximumHours>48</MaximumHours>
</AvailabilityAttributes>
<IsEligibleForSuperSaverShipping>0</IsEligibleForSuperSaverShipping>
<IsEligibleForPrime>0</IsEligibleForPrime>
</OfferListing>
</Offer>
</Offers>
</Item>
</Items>
</ItemLookupResponse>
I have tried many permutations of the code which has lead me to the conclusion that removing the 'ItemLookupResponse' node gives me the error 'multiple root elements'. But removing the 'ItemLookupResponse' and 'OperationRequest' gives me working code.
Using my adapted 'query' (above) on the full XML response gives me the error 'Object reference not set to an instance of an object.'
I would be very grateful if anyone could point me in the right direction to run my adapted 'query' on the full XML, and overcome the 'Object reference not set to an instance of an object' error.
Many thanks, James
Upvotes: 1
Views: 1647
Reputation: 11228
You can use LINQ to XML - this will return the formatted price for a given ASIN value:
var res = XElement.Parse("data.xml")
.Descendants("ASIN").FirstOrDefault(elem => elem.Value == "12345")
.Parent.Descendants("FormattedPrice").Select(elem => elem.Value)
.FirstOrDefault();
If you want to get all ASINs in a document:
var asins = XElement.Parse("data.xml")
.Descendants("ASIN")
.Select(elem => elem.Value)
.ToList();
Edit - the problem you have arises because you omit namespaces - check LINQ to XML namespaces on msdn.
XNamespace ns = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
var res = XElement.Load("data.xml")
.Descendants(ns+ "ASIN").FirstOrDefault(elem => elem.Value == "B001MS70F2")
.Parent.Descendants(ns + "FormattedPrice").Select(elem => elem.Value)
.FirstOrDefault();
Console.WriteLine(res); // prints L10.49
Also have a look at this question on SO: XElement namespaces (How to?)
and this on msdn: https://msdn.microsoft.com/en-us/library/system.xml.linq.xnamespace(v=vs.110).aspx
Upvotes: 0
Reputation: 26213
I'd parse the data using LINQ to XML and then create a query like this:
var prices = from item in doc.Descendants("Item")
from summary in item.Elements("OfferSummary")
from lowestNewPrice in summary.Elements("LowestNewPrice")
select new
{
ASIN = (string) item.Element("ASIN"),
FormattedPrice = (string) lowestNewPrice.Element("FormattedPrice")
};
You could then convert this to a dictionary, or simply query for your ASIN:
var price = prices.Where(x => x.ASIN == "12345")
.Select(x => x.FormattedPrice)
.Single();
See this fiddle for a working example.
Upvotes: 0
Reputation: 18127
static void Main(string[] args)
{
string xml = @"<ParentNode>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<Item>
<ASIN>B001MS70F1</ASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1098</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£15.98</FormattedPrice>
</LowestNewPrice>
</OfferSummary>
</Item>
</ItemLookupRequest>
</Request>
</Items>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<Item>
<ASIN>B001MS70212</ASIN>
<OfferSummary>
<LowestNewPrice>
<Amount>1098</Amount>
<CurrencyCode>GBP</CurrencyCode>
<FormattedPrice>£10.98</FormattedPrice>
</LowestNewPrice>
</OfferSummary>
</Item>
</ItemLookupRequest>
</Request>
</Items>
</ParentNode>";
XDocument doc = XDocument.Parse(xml);
List<XElement> elements = doc.Descendants("Item").ToList();
foreach(XElement elem in elements)
{
string asin = elem.Element("ASIN").Value;
string formatedPrice = elem.Element("OfferSummary").Element("LowestNewPrice").Element("FormattedPrice").Value;
//you can use this for formated price too
string formatedPrice2 = elem.Descendants("FormattedPrice").FirstOrDefault().Value;
Console.WriteLine("ASIN: " + asin + " and Price:" + formatedPrice);
}
Console.ReadKey();
}
Here an example program, next time atleast post valid xml. You need to add using System.Xml.Linq;
reference
Upvotes: 0