Using Amazon’s Web Services Toolkit in Your Windows Forms Applications

arlier this year, Amazon quietly exposed a set of Web services that can be used to allow any application to find and display Amazon product information. The Web services are compatible with any language capable of posting and receiving data via HTTP and consuming either HTML or SOAP-based XML.

Amazon’s Web Services 2.0 let you build query and storefront applications that use the services to supply up-to-date information on Amazon products. The free download includes a number of examples?including some complete applications?that show how to use the services from Java, .NET, PHP, and VB/VBA.

Getting Started
First, download the toolkit and unzip the result. That creates a directory structure containing the files you’ll need for development. Next, navigate back to Amazon’s download site and click the Apply For a Free Developer’s Token link. You must send your developer’s token along with each request. You cannot run the sample application without a developer’s token.

There are two basic methods for querying an Amazon Web service: with an HTTP request (XML over HTTP or REST) and via SOAP. In addition, you can pass an XSLT stylesheet along with the request and get back HTML (or other XSLT output) formatted according to your needs. The XSLT capabilities are currently available only via the REST method, not through SOAP calls. However, that doesn’t really matter much for most applications, because you’re perfectly free to apply your XSLT stylesheet locally to the SOAP response.

One of the nicest things about Web services is that you don’t need to write browser-based applications to take advantage of them. Amazon’s Web services are capable of retrieving information about a wide range of Amazon’s products and services; however, once you get the idea, they’re all similar. It’s a little easier to apply the toolkit results to a Web application, and the sample applications in the toolkit show how to do that; so in this article, you’ll focus on building a Windows Forms-based query engine that lets users search for programming books and display the results.

Building the Sample Application
The basic plan is to provide a search field where the user can enter keyword search terms and then to call the Amazon service to search for books using those keywords. The application should display a sorted list of titles matching the search request. When the user clicks a title in the list, the application will retrieve the details for the selected title, and display them.

To do this, you need to make two types of requests: a KeywordRequest and an AsinRequest. ASIN is shorthand for “Amazon.com Standard Item Number”?a unique ID assigned to every Amazon product.

Each request has a type, which you can set to the string “heavy” or “lite,” and controls the detail level of the returned data. For this application, a “lite” request suffices for the keyword request because you’re only going to display titles that match the search request. The lite request type also returns the ASIN, which you can then use to perform an AsinSearchRequest using the “heavy” request type to get all the details for a specific item.

Create a new Windows Forms application and add these items:

  • TxtSearch?a single-line TextBox for the user to enter keyword search terms.
  • LstResults?a ListBox to hold the results of the keyword search query.
  • PnlDetails?a Panel that will hold controls to display the details returned from an AsinRequest.

Next, you need to add a Web Reference to the Amazon Web services. To do that, right-click on the Web References item in Visual Studio’s Solution Explorer, select Add Web Reference, and enter the following URL in the Address field of the Add Web Reference dialog:

http://soap.amazon.com/schemas2/AmazonWebServices.wsdl

Press Enter to access the WSDL file for the service. You’ll see the results shown in Figure 1.

Figure 1. Creating the WSDL: The Add Web Reference dialog shows the WSDL for the Amazon Web services.

When you do that, VS.NET creates a proxy file (reference.cs) that defines all the classes you need to make SOAP requests and retrieve data from the responses.

Using the Amazon Web Services
After adding the Web Reference, using the services is straightforward. Add a using (C#) or an imports (VB.NET) statement to the top of your form, so you don’t have to type the complete namespace string to access the classes.

C#using Amazon.com.amazon.soap;VB.NETImports Amazon.com.amazon.soap

To make a SOAP query, you first create an instance of the AmazonSearchService class.

AmazonSearchService srch = new AmazonSearchService();

Next, create a class for the type of query you want to make.

Each query type has an associated class; for example, to perform a keyword search, create a new KeywordRequest instance; for the ASIN search, create an AsinRequest object. Next, you set the properties for the specific request. Finally, you pass the populated request to the appropriate method of the AmazonSearchService. For a keyword search, call the KeywordSearchRequest method. The names of the request classes and the AmazonSearchService’s request methods match, so it’s easy to match request types and request methods.

Most request types return a ProductInfo node in an XML document that looks similar to the KeywordSearchResponse document shown in Listing 1. The SOAP infrastructure handles mapping the returned SOAP elements to the classes provided by the proxy. For example, the element in the response contains response information, including a list of

elements that contain the data for individual products. You create a ProductInfo class instance to retrieve the ProductInfo from the response, and then iterate through the list of Details to retrieve the item data for display.

KeywordRequest kr = new KeywordRequest();kr.devtag="Your Developer Token here";kr.keyword=this.txtSearch.Text;kr.mode="books";kr.sort="+titlerank";kr.tag="webservices-20";kr.type="lite";kr.page="1";ProductInfo pi = srch.KeywordSearchRequest(kr);Details[] allDetails = pi.Details;

You must pass your Developer Token for each request. You can find valid values for the other parameters for the KeywordRequest (and all request types) in the documentation that accompanies the Amazon download, but the names are fairly intuitive. The example above searches for books containing the keywords the user entered in the txtSearch TextBox on the form, sorts the results alphabetically by title in ascending order, and returns the “lite” response type containing information for the first 10 results (kr.page=”1″).

Each query returns at most 10 items, so you need to be able to make multiple queries to get all the data for any particular keyword search. Each query that can return multiple values sets a ProductInfo.TotalResults property that you can use to determine whether the query has more possible results. For example, in the preceding example, a TotalResults value of 15 would mean that you could requery the Web service and request page 2, which would return the last five items:

if (pi.TotalResults > 10)// get the second page of resultskr.page="2";ProductInfo pi = srch.KeywordSearchRequest(kr);Details[] allDetails = pi.Details;

Displaying Query Results
The Details class exposes the detail fields for each item returned. It’s a generic class that contains fields that apply to all items rather than only books, so not all queries populate all the fields. For example, here’s a portion of the class as VS.NET initially creates it (see the reference.cs file in the sample code for the complete listing).

[System.Xml.Serialization.SoapTypeAttribute("Details",    "urn:PI/DevCentral/SoapService")]public class Details {          public string Url;   public string Asin    public string ProductName   public string Catalog;   public KeyPhrase[] KeyPhrases;   public string[] Artists;   public string[] Authors;   public string Mpn;   // more field definitions...}

In the sample code, you want to display the Details.ProductName field in a ListBox. Because the list of Details is simply an array of Details objects, you should be able to assign the array directly to the ListBox.DataSource property, and set the ListBox.DisplayMember property to the ProductName field. Unfortunately, that doesn’t work. The DisplayMember property only works for methods, not for publicly exposed fields.

The simplest workaround is to change the public member fields to private variables and then add public property accessors for those variables to the value you want to display. For example, in the sample reference.cs file, you’ll see that the Details class has been altered with the ProductName field implemented as a true property rather than a public member, as shown below.

   public string ProductName   {      get       {         return mProductName;      }      set       {         mProductName = value;      }   }

After making that slight modification, you can set the ListBox properties to display the data.

ProductInfo pi = srch.KeywordSearchRequest(kr);Details[] allDetails = pi.Details;this.Cursor = Cursors.WaitCursor;this.listBox1.BeginUpdate();this.listBox1.DataSource = null;this.listBox1.Items.Clear();         this.listBox1.DataSource=allDetails;this.listBox1.DisplayMember="ProductName";this.listBox1.SelectedIndex=-1;this.listBox1.EndUpdate();this.Cursor = Cursors.Default;
Author’s Note: Depending on your needs, you may want to define a custom class to populate bound controls rather than altering the generated classes. Also, although the preceding code doesn’t show error-trapping, watch out for errors?the Details array may be empty, which causes an error when you assign it to the ListBox.DataSource. Also, the KeywordSearchRequest can fail if the service is not available, or if the request is invalid.

Using the altered class, you can now perform a KeywordSearchRequest and display the results in a ListBox. There are two other minor problems. It can take a few seconds to get the data via the Web service. Because you don’t want the user clicking on items during that time, let the user know that an action is in progress by changing the cursor and using the BeginUpdate/EndUpdate methods to disable the ListBox display for the duration of the search request and the update.

Returning Item Detail
That completes the first portion of the application. The second requirement is to get the detail information when a user clicks one of the titles. To do that, use the ListBox’s SelectedIndexChanged event to retrieve the ASIN number of the selected item, and then perform an AsinSearchRequest to get the detail data.

// create a new AsinRequest objectar = new AsinRequest();// get the ASIN number from the Details item stored// in the ListBoxar.asin = ((Details) listBox1.SelectedItem).Asin;// set propertiesar.devtag=devtag;ar.type="heavy";ar.tag = "webservices-20";// perform the searchpi = amazonSrch.AsinSearchRequest(ar);Details[] allDetails = pi.Details;// code to display detail here

This time, you want to perform a “heavy” search to return all the details. After retrieving the details, the sample code displays a selected subset by concatenating the information using a StringWriter, and then assigning it to the txtDetails TextBox on the form.

StringWriter sw = new StringWriter();StringBuilder sb = new StringBuilder();         if ((allDetails != null) && (allDetails.Length > 0)){   Details d = allDetails[0];   sw.WriteLine("Title: " + d.ProductName );   sw.WriteLine("Asin: " + d.Asin);   String[] authors = d.Authors;   if (authors != null)    {      if (authors.Length > 1)       {         sw.Write("Authors: ");      }      else       {         sw.Write("Author: ");      }      for (int i=0; i < authors.Length; i++)        {         if (i>0)          {            sw.Write(", ");         }         sw.Write(authors[i]);      }      sw.WriteLine("");   }   sw.WriteLine("Availability: " + d.Availability);   sw.WriteLine("Browselist: " + d.BrowseList);   sw.WriteLine("Catalog: " + d.Catalog);   sw.WriteLine("CollectibleCount: " + d.CollectibleCount);   sw.WriteLine("CollectibelPrice: " + d.CollectiblePrice);   sw.WriteLine("Distributor: " + d.Distributor);   sw.WriteLine("ImageUrlSmall: " + d.ImageUrlSmall);   sw.WriteLine("ImageUrlMedium: " + d.ImageUrlMedium);   sw.WriteLine("ImageUrlLarge: " + d.ImageUrlLarge);   this.txtDetail.Text = sw.ToString();   sw.Close();   // more code here}

Displaying Images from a URL
The final task is to display the image. The ASIN request returns image URLs for large, medium, and small images, when available. The best size for the sample application is the medium image. Unfortunately, there’s no simple method call to retrieve an image from a URL. Instead, you need to make a separate request to get the image data. The ImageFromURL() method in the sample application solves that problem.

// add a using System.Net reference to your class using System.Net;// retrieve an image from a URLprivate Image ImageFromUrl(String url) {   WebClient wc = new WebClient();   Stream st = wc.OpenRead(url);   Image im = Image.FromStream(st);   st.Close();   return im;}

The ImageFromUrl method creates a WebClient instance and uses it to request the image data from the specified URL, assigns that to a Stream, and then loads the Image from the stream and returns it. You can assign the returned image directly to the PictureBox.Image property (see Figure 2):

// the variable d is the instance of the Details class filled with data returned// from the AsinSearchRequest method.this.pictureBox1.Image = this.ImageFromUrl(d.ImageUrlMedium);
Author’s Note: The application only displays the medium-sized image, when it’s available. Not all books have associated images?and of those that do, the image may not be available. Missing images don’t cause an error, they simply don’t display.
Figure 2. The sample application showing the details and an image for the item selected in the upper list.

This sample application is probably enough to get you started, but you can go far beyond this simple example and write applications that notify users when new books become available, monitor the sales rank of items on Amazon, retrieve Wish Lists and Marketplace information, and add transactional purchasing, including Amazon’s “Quick-Click Buying” capability to your applications and Web pages.

You should be aware that there are some limitations attached to these free developer services. For example, as a developer, you’re not allowed to make queries more than once per second, nor make queries containing more than 20K of data (which seems eminently reasonable). You agree to refresh cached information periodically; the refresh interval depends on the type of information. Read the Amazon.com_License.txt file carefully before you decide to use the services in your applications.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: