KOKOGIAK

GEDANKENGANG

Consuming Amazon's Web API Directly with Javascript (via JSON and XSLT)

Subtitle: AMZN-XSLT-JSON-AJAX (AXJA?)

Amazon.com's Web API is a cool piece of architecture, but one of its big limitations is the difficulty involved in getting Amazon's data straight to the browser. Cross-domain security restrictions prevent XMLHttpRequest (the cornerstone of AJAX) from being able to call up third-party URLs, and XML can be a fairly fussy beast to parse using Javascript (do-able, but not simple). In other words, it stinks that you can't easily call up Amazon's API from a web page on "kokogiak.com" without going through your own separate (proxy) server first.

Well, here's how to fix that problem (for Amazon.com's API at least), using Amazon's free XSLT transformation service, the JSON output format, and a bit of plain old javascript. The examples below show you how to use javascript (and a bit of XSL) to place live Amazon.com data - direct from Amazon's servers - on any web page.

Amazon's XSLT (Extensible Stylesheet Language Transformation) service

For those who don't already know, XSL is a language for transforming XML documents (into other documents/document types). Since Amazon's API output is essentially XML documents, XSL is useful to transform those documents into HTML, or plain text, or different XML documents or - in our case - javascript. It's just text after all. Also, you can define not only the output, but the content-type of the output as well.

(Aside: Back when Amazon's Web Services was in version 3.0, I built an entire alternate Amazon Interface based on just 2 files - one XSL template and one CSS stylesheet. It's still kicking over here)

JSON

JSON (JavaScript Object Notation) is a small, lightweight data-interchange format. Basically that means it's a tidy and simple way to use javascript to create objects made up of name : value pairs. A JSON Object can be as simple as:

var fruitstand = { 'fruitA':'apple', 'fruitB':'orange' }

Where 'fruitstand' is an object made up of apples and oranges. If I wanted to find out the value of 'fruitB' in javascript, I'd just have to write:

window.alert(fruitstand.fruitB)

and I'd get a popup alert that would say "orange".

For a more informative and in-depth examination, see Dustin Diaz' JSON for the Masses.

Combining the Two (XSLT and JSON)

Using Amazon's XSLT service to transform its own XML output into JSON objects makes it very easy to dynamically include this data in a web page. You can do this by using the DOM to create and append a new SCRIPT element to the document, which will be evaluated as it loads onto the page.

Huh? Well, let's go to the examples for some clarity.

The Examples

1. Display a single Amazon Item on a page (rendered on pageload)
2. Display a "Canned" Top-5 keyword search in a single category (rendered on pageload)
3. Show "Top Sellers" within a single department (rendered on pageload)
4. Show a "Top-10" keyword search in a single category (user-initiated)
5. Amazon.com Storewide search with pagination (user-initiated)


1. Display a single Amazon Item on a page (rendered on pageload)

Example 1 shows a simple DIV containing a linked product image, title, price and description on a page.

If you view-source on that page, you'll see that there is a variable named "request" near the bottom, which is the URL that is used to fetch the JSON data (and callback function name). Because this URL is so central to this process, here's an anatomy:

The whole enchilada:
http://xml-us.amznxslt.com/onca/xml?Service=AWSECommerceService&
SubscriptionId=19267494ZR5A8E2CGPR2&AssociateTag=kokogiak7-20&
IdType=ASIN&ItemId=159184021X&Operation=ItemLookup&
ResponseGroup=Medium,ItemAttributes,OfferFull&
Style=http://kokogiak.com/amazon/JSON/ajsonSingleAsin.xsl&
ContentType=text/javascript&CallBack=amzJSONCallback


The pieces:

A. http://xml-us.amznxslt.com/onca/xml?Service=AWSECommerceService - Amazon.com's XSLT Service URL. It's a little odd-looking, but go ahead and do a whois lookup, it's owned by Amazon. (The domain isn't "amazon.com" for security reasons)

B. SubscriptionId=19267494ZR5A8E2CGPR2&AssociateTag=kokogiak7-20 - Personal Identifiers. The first one is my Access Key - so Amazon can track use (and abuse) of the service. Please be kind and use your own Access Key and don't abuse mine, thanks. The Associate Tag is there so any URL links to Amazon products will have my Associate ID in them (and any possible Associate referral fees as well). While I don't mind your use of my associate ID, you'll probably want to use your own as well.

C. IdType=ASIN&ItemId=159184021X - What type of ID are we using (ASIN, UPC or other)?, and what is the ID to lookup ('159184021X', the ASIN for "Purple Cow: Transform Your Business by Being Remarkable")

D. Operation=ItemLookup - tells the service what we're about to attempt, in this case, an Item Lookup.

E. ResponseGroup=Medium,ItemAttributes,OfferFull - Tells Amazon the extent of the data we'd like to get back

F. Style=http://kokogiak.com/amazon/JSON/ajsonSingleAsin.xsl - tells the XSLT service where to find my XSL file to use for transformation in this instance.

G. ContentType=text/javascript - instructs the XSLT service to return the resulting text with this content-type.

H. CallBack=amzJSONCallback - Tells the XSLT service the name of our JSON callback function. One of the neater parts about Amazon's XML Web Service is argument reflection - any value passed into the service is reflected back, and can be incorporated into any XSL output. In this case, I made an arbitrary name "CallBack", so the value "amzJSONCallback" can be used by the XSLT service to build a javascript function call.

If you look at the transformed JSON returned by this URL above, you'd see the function call that is returned below (a couple of lengthy strings clipped for this display):

amzJSONCallback( { "Item" : {
"asin":"159184021X",
"url":"http://www.amazon.com/exec/obidos/redirect?t...clip...CGPR2",
"title":"Purple Cow: Transform Your Business by Being Remarkable",
"thumburl":"http://images.amazon.com/images/P/159184021X.01._SCTHUMBZZZ_.jpg",
"thumbdims":["60","43"],
"price":"$12.97",
"lowestnewprice":"$6.66",
"lowestusedprice":"$4.99",
"desc":"You're either a Purple Cow or you're not. You're either...clip...in the first place."}
} )


So, when the script is inserted into the HEAD of the page (on page load), the function call above is executed, and the data gathered straight from Amazon is displayed in-page.

There's also a stripped-down version of Example 1 here: Example 1A, with no comments and many of the functions rolled up into single statements, presented mainly as an example of compactness (less than 2.5k of JS).


2. Display a "Canned" Top-5 keyword search in a single category (rendered on pageload)

Example 2 shows a DIV containing a list of the Top 5 books based on a "canned" keyword, in this case, "George Foreman". Again, view-source is your friend here, you'll see that this differs only slightly from example #1, using a different XSL stylesheet, and a slightly different URL to call Amazon's XSLT service.


3. Show "Top Sellers" within a single department (rendered on pageload)

Example 3 shows a DIV with the top 10 bestselling Lawnmowers. Again, not all that different from Examples #1 & #2. The trickier bit here is "How do I find out what the Browse Node and Category are?" In this instance, I browsed to the category page I wanted on Amazon, and found the browse Node ID at the very end: http://www.amazon.com/gp/browse.html/103-4189577-2632619?%5Fencoding=UTF8&node=553940 - and I know already that it's in the "Tools" category. For more info about Amazon's Web Services BrowseNode/Category definitions, see this page


4. Show a "Top-10" keyword search in a single category (user-initiated)

Now for the fun "asynchronous" bit - you can create and insert numerous scripts, so, that means you can call the same Amazon URL (with different keywords, etc) and the same callback function, so you can do fun things like user-initiated search in-page. Example 4 is a simple book-search, where you can enter a keyword (it's pre-seeded with "da vinci"), and get the top 10 results straight from Amazon, in-page.


5. Amazon.com Storewide search with pagination (user-initiated)

By far the most complicated of the examples, Example 5 is a full store-wide Amazon search interface. Combining the two XSL stylesheets used above, with one more XSL stylesheet for "blended" (store-wide) searches, you can search through (yet another) mini-amazon.

Enter a keyword, and get a list of categories and how many hits in each. Click on a category and get a list of results that can be expanded 10 at a time (amazon's restriction, not mine). Click on an item, and see the image, description and link pop into place. I could almost call this example "Amazon Light v5.0" (but I won't).

That's all of the examples in action. Now, here are the three XSL files being used:

ajsonSingleAsin.xsl
ajsonCategorySearch.xsl
ajsonBlendedSearch.xsl

The biggest trick in these XSL files has been the use of hobbled XSL Search-and-replace methods to weed out carriage returns and stray quotation marks that might break the JSON code block. Also, for the Search Results files, I added an empty entity at the end of the serialized block of data: {"endBuffer":"true"} - mostly to clean up any trailing commas.

Next Steps

I'd like to next create a single all-purpose JSON XSLT stylesheet for Amazon ASINs - one that can accommodate Lawn Mowers, DVDs, Magazines, Toilet Paper and more.

I'd also like to create an "Amazon JSON Builder" wizard, to allow people to build their own JSON Amazon content, and generate some script for copying & pasting.

Any other suggestions or comments are welcome and encouraged, thanks.


Further reading:
Amazon's XSLT Service
JSON
Amazon's Web Services Documentation
Dustin Diaz' JSON for the Masses
Dan Theurer's Against the Browser's Will: Make Mashups Talk Across Domains
Simon Willison's JSON and Yahoo!'s JavaScript APIs
Jason Levitt's Fixing AJAX: XMLHttpRequest Considered Harmful
Darryl Lyons' AJAX with dynamic SCRIPT tags -- revised
Yahoo's Yahoo! Web Services with JSON

23 Comments +

Hi Alan,

The free TicTap Contextual Ads uses JSON from XSLT too!
http://www.tictap.com/tca_labs/instructions.html
by Anonymous Alex Choo at 2:44 AM 
Wow. This is pretty amazing stuff. It's stuff like this that will enable the next step in "mashups" and user control of user generated content. Thank you for this.
by Anonymous Erik Kasnter at 5:24 PM 
does anybody have a sample code of web services that return JSON? I am trying to use this example against my own web services and it does not work as my ASP.NET web service is wrapping xml around my generated JSON text.
by Anonymous Raunaq at 5:08 PM 
See http://weblogs.asp.net/mschwarz/archive/2006/07/11/Yahoo_2100_-Web-Services-Request-and-AjaxPro-JSON-Parser-_2D00_-I-love-it_2100_.aspx
by Anonymous Anonymous at 5:25 AM 
Hi Alan-
Just a quick note to say thanks for posting this hack - I gave it a whirl with my Amazon wishlist to get the data into a state where I could consume it client side and plug it into Bill Scott's YUI carousel component (http://billwscott.com/carousel/). The result is described here:
http://blogs.open.ac.uk/Maths/ajh59/007299.html
tony
by Anonymous Tony Hirst at 12:35 PM 
Great. Thanks for the information.
by Anonymous Johannes Voetter at 11:43 AM 
Thank you for posting this. This was exactly what I was looking for! Amazon's code samples are skimpy at best and this really helped me understand the possibilities with AWS ECS and XSLT.
by Anonymous Stella at 1:12 AM 
Hi,
Liked the pennies project mention, very cool.
Do you want to include http://www.2200ad.com?

It is a forum where people debate about what the world will be like in 2200AD.

Free site, no advertising.

SJG
by Anonymous SJG at 9:47 PM 
nice site and very useful
by Anonymous Andy martin at 12:47 PM 
i like the design of this blog, is very simple, and useful.
by Anonymous compuntoes at 7:44 PM 
Hi Alan. Great Stuff!! A touch beyond be. You understand.
by Anonymous Dan at 9:13 PM 
Great stuff. I am trying to return one result. I am trying to create the url you have shown examples with but different variables[code]$params = array( 'TextStreamSearch' => $song_title,
'mode' => 'music',
'sort' => 'titlerank',
'page' => 1,
'type' => 'lite',
'f' => 'http://www.convergenttech.com/skid/amazon.xsl',
't' => 'xxxxxxxx',
'dev-t'=> 'xxxxxxx',
);

My amazon.xsl is a generic one that returns ten results. I want just ten, and the exact match based on "$song_title" which is artist and song. Any suggestions? scott_paxton@scottpaxton.com
by Anonymous Scott P at 11:24 AM 
I meant I want just 1(one) result.
by Anonymous Scott P at 11:25 AM 
Wicked Resource! Thanks a bunch for this easy to understand and implement explanation of a powerful programming language.
by Anonymous tropicaltalent at 4:50 AM 
Would these techniques work for creating an RSS feed for an Amazon Webstore? I've been trying to play with the documentation and came across your site.
by Blogger Desert Rose Bulldogs at 7:02 PM 
yes, it will work. for more info, just contact...
by Anonymous Druckerei at 5:55 AM 
Would it be possible to modify the script to read a list of n ASIN values?
by Blogger Jay Morgan at 11:02 PM 
Thx for this post!
by Anonymous Flyer drucken at 5:58 AM 
Really usefull, thanks!
by Blogger Alex Harvey at 1:53 PM 
Wonderful article!


Question I have:


Is there a way to change to
ResponseGroup=Large?
When I change it I get Medium instead. Or maybe there is another way - my goal is to get the author.
For instance:

http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&SubscriptionId=19267494ZR5A8E2CGPR2&AssociateTag=kokogiak7-20&Operation=ItemLookup&Style=http://kokogiak.com/amazon/JSON/ajsonSingleAsin.xsl&ContentType=text/javascript&IdType=ASIN&ItemId=1442141018&ResponseGroup=Large&CallBack=amzJSONCallback


Bravo again.
by Anonymous Piotr at 1:22 PM 
Hi, awesome bit of code. Helped me out alot. Quick question, how does one go about styling the different elements, like the description, price etc?

Thanks,

r
by Anonymous ruan at 12:42 PM 
Hi,

Godd stuffs. However, Amazon requires to sign the API, so this example does not work anymore. Any idea/suggestion?

thaks
by Blogger QicRewarder at 9:48 AM 
I think the Amazon web service has changed. Do you know how to adjust your excellent examples to work with the new changes ?!

Best wishes
Tryfon
by Blogger kingscrusher at 7:51 PM 
Archives: