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 
del.icio.us links for 05-20-06

1 Comments +

really a great resource, i ll be glad to suggest it to all my friends!
by Anonymous ansia at 4:33 AM 
del.icio.us links for 05-19-06

2 Comments +

hola,de quien es esta pagina
by Anonymous chile at 9:01 PM 
nice blog
look my blog
www-franco.blogspot.com
by Anonymous Anonymous at 2:32 PM 
del.icio.us links for 05-17-06

0 Comments +

del.icio.us links for 05-16-06

0 Comments +

del.icio.us links for 05-13-06

0 Comments +

del.icio.us links for 05-12-06

0 Comments +

del.icio.us links for 05-11-06

0 Comments +

del.icio.us links for 05-10-06

0 Comments +

del.icio.us links for 05-06-06

0 Comments +

del.icio.us links for 05-05-06

0 Comments +

del.icio.us links for 05-04-06

0 Comments +

del.icio.us links for 05-03-06

0 Comments +

del.icio.us links for 05-02-06

1 Comments +

loved The MegaPenny Project !
by Anonymous moon at 4:41 AM 
The making of kokogiak.com, version 05.01.06

or: Mashing up Blogger, Yahoo, Del.icio.us, MSN and My stuff using AJAX, JSON, XHTML and ASP/PHP
or: Buzzword-bingo webdev


Prologue

As part showcase, part playground, part repository, I really live in this website, especially this page. As such, I've been having a lot of fun lately, tailoring my surroundings to my needs. My main overriding goal for this redesign was accessibility (in the general sense) and clarity. I am happy that I've achieved my goal of readability and clarity with the layout, color and look here. But of course, I couldn't just teak the look, I had to poke around at the guts of the thing, making use of all appropriate APIs and technologies along the way.

Technologies used

Kokogiak.com reflects my work, but runs on the back of quite a few technologies and services. The site uses:

- Blogger.com for the weblog and comments, with a number of hacks acting as intermediary to get the look and interaction I wanted
- Yahoo's News Search, incorporated into this site using JSON
- Yahoo's Animation Library for the open/close sliding effects
- Del.icio.us for low-threshold links and bookmarks, also incorporated here using JSON
- Google's Mapping API for the "where" tab
- MSN Search to do site-search
- An IIS server, yet it uses very little actual ASP code (which is a good thing)

Blogger:
I've been using Blogger for a few years now, and am pretty happy with it - the archiving, ease of posting, comments - and especially the price structure (free). Unsatisfied with the static nature of the HTML emitted by blogger, I just built my main template as an HTML blob meant to be included server-side in a larger page (with ASP). See the plain HTML page here, and in context here (the right column).

Ajax-ifying Blogger comments. While I'm happy they are available, I never liked the way Blogger sends visitors 'offsite', to Blogger.com, in order to post comments on my blog. Using a bit of AJAX sleight-of-hand and an ASP page to act as proxy, I was able to make the Blogger comments work in-page, and within the design and context that I wanted.

When a visitor submits a new comment, the data from the form is passed via AJAX to a proxy page I built on kokogiak.com: poster.asp (bypassing cross-domain limitations). Poster.asp then uses Microsoft's server-side XMLHTTP object to pass along the data once again to Blogger.com via POST, returning the body of Blogger's "thank you" page. If successful, a message is sent back to the original page, kokogiak.com, and the form contents are automatically updated in-page with the submitted comment.

Drawbacks: Non-valid HTML in comments. Grrr. Blogger uses invalid HTML in their comments (things like <BR/>), so unless I can figure a hacky way around it (or they fix it), I won't be able to have an XHTML compliant blog while using blogger comments.

Yahoo!:
Yahoo's Web APIs are really great, and I'm using three of them here.

For the "News" tab (at top left on the main page), I'm using a few canned searches, like "xml" and "javascript+ajax", and simply using their JSON-formatted webservice to fetch, return and display the results in-page. Really sweet. I also use the JSON ScriptBuilder (found here) to create dynamic scripts and fetch the results.

To create the smooth open/close action that happens when you click "More+" or "Comments" links, I'm relying on Yahoo's Animation Library, part of the Yahoo! User Interface Library. I've never been a huge fan of canned script libraries like scriptaculous or dojo - mostly out of ignorance and pig-headedness, but often because they never seem to be as flexible as I want. That said, Yahoo's Animation Library really does a nice cross-platform job for me, and I'm quite happy to use it.

For the Del.icio.us links (remember, del.icio.us is part of the Yahoo! Family now), I am once again using JSON to fetch and display the links in-page. The main difference from the Yahoo! News JSON is that I already included the JSON output from del.icio.us, so the data is already in the page's memory on pageload, awaiting a click to be formatted and displayed.

Google Maps
Plenty has been written elsewhere about incorporating Google Maps into sites. About the only different things happening here are small rounded corners (top left and top right) laid over the map to make it fit the format a little better, and the dynamic creation/destruction and reveal of the map when the "where" tab is opened or closed.

The rounded corners are simply two small transparent gif images, placed after the map DIV, with negative CSS positioning to lay them on top. The dynamic creation/destruction is in part, simply the Google Map API (using their new GMap2 constructor), and part, the simple, yet effective innerHTML sledgehammer. Setting the innerHTML of the container DIV to "" effectively kills the map when another tab is clicked.

MSN Site Search

Once again, an ASP page on my domain using the server-side XMLHTTP object to fetch MSN RSS search results and display them here, in-page, all ajaxy. Why MSN? The quality of their results was the best. Plain and simple.

IIS Server

Why IIS? Why not PHP? Mostly because of cost, simplicity and laziness. I've had my site on an IIS server for many years now, with Speakeasy Webhosting and the deal is very nice. I've learned to live with the shortcomings of ASP, and how to augment it or abandon it when I need to.

Reference / Inspiration:

For legibility and layout, I consider Khoi Vinh's site subtraction very inspirational. Vinh's structure and layout and simplicity was a great touchstone when I was originally brainstorming the design.

For javascript and AJAX inspiration (and some code snippets), I was really inspired by Dan Theurer's Against the Browser's Will: Make Mashups Talk Across Domains article.

For some of the CSS Image Replacement techniques, I looked to StopDesign, and Petr Stanicek (Pixy) and Tom Gilder's Cover-up Method.

For the Yahoo! News API, I looked to the official API docs.

For the yahoo Animation API, I also referred to their official Yahoo User Interface Library API docs

For the Del.icio.us bits, I spent plenty of time in the "Tools" section of their Help Page

For Google Maps, nothing beats their official API documentation.

Background Image, courtesy of NASA/JPL-Caltech, and the Cassini Probe, the background image for this site is a view of Saturn's Rings, seen over its horizon. The upper atmosphere distorts the light, bending the rings ever-so-slightly. Original image also here. The (inserted) moon is Saturn's Dione.

2 Comments +

I love the drop shadow effects and the fluid animation of the comments sections. I am always thoroughly impressed with your work.

I was actually working on a drop shadow look and feel for my own blog a few months back. See here.

Very nice work!
by Anonymous Jonathan at 1:16 PM 
Nice
by Anonymous Jason at 8:46 PM 
The new look

It's here, my promised redesign. RSS Readers, please stop by the site and have a look, and let me know your thoughts. For now, I must head off to sleep.

I'll go further into the details in another post, but I'm really happy with the readability of this version, vs. the older version (roughly archived here). It is nearly fully XHTML (save for Blogger's nonstandard comment markup). The fluidity of the layout and the compactness of entries (the hide/show widgets) were also important pieces for me to get as well.

Have fun - play with the "who, what, where, etc" links at top left. Criticism is invited, please let me know your thoughts on the look, feel and function of kokogiak.com, version 05.01.06 .

-Alan

1 Comments +

i like it. slick, w/o being too flashy. well done.
by Anonymous stephen ogrady at 4:52 AM 
Archives: