Charles Engelke's Blog

December 28, 2011

Chrome Web App Bookshelf – Part 4

Filed under: Uncategorized — Charles Engelke @ 4:25 pm

Note: this is part 4 of the Bookshelf Project I’m working on. Part 1 was a Chrome app “Hello, World” equivalent, part 2 added basic functionality, and part 3 finally called an Amazon web service. This part will finally parse the web service result and refine the call.

When I got to the end of the last post, the app was just dumping a lot of XML, formatted as text, into the web page. I want to pull just the data fields I’m looking for, format them, and put that in the page instead. Those fields are: author, title, release date, availability, list price, Amazon’s price, and a link to the Amazon page. So I copied the text from the page to a file and viewed it in a program that showed the XML as an outline. It’s way too big to show here, but the structure of the response looked like this:

  • ItemLookup
    • Operation Request
    • Items
      • Request
      • Item
        • ASIN
        • DetailPageURL
        • ItemAttributes

It’s that Item element that actually has the response in it, with fields inside ItemAttributes containing most of the information I want. I see Author, Title, PublicationDate, and ListPrice inside of ItemAttributes, and DetailPageURL has the link I need. But I’m missing the availability and Amazon’s price. So back to the ItemLookup function’s documentation, which I’m a bit more ready to understand now. My request can include a specification of one or more ResponseGroup values. The default is just ItemAttributes, which is what I got in this sample response. But other groups might have the two fields I’m missing. After browsing through the documentation, I see that Offers includes both the Amount and Availability, so I’ll add that to my request. Doing so is easy: just change the line that specifies the requested ResponseGroup to:

      params.push({name: "ResponseGroup", value: "ItemAttributes,Offers"});

The resulting XML has what I need, structured as follows:

  • ItemLookup
    • Operation Request
    • Items
      • Request
      • Item
        • ASIN
        • DetailPageURL
        • ItemAttributes
        • OfferSummary
        • Offers
          • Offer
            • OfferListing

The extra information I’m looking for is in the OfferListing element, which contains Price and Availability fields. The Price field (like the ListPrice field mentioned earlier) is itself complex, containing an Amount (an integer, apparently equal to the whole number of cents), a CurrencyCode (USD in my example), and a FormattedPrice. I’m going to go with the FormattedPrice field for now, but I may want to change my mind later.

Okay, this XML has the data I want, how do I get to it? This is the job of extractAndReturnResult, which currently looks like:

      function extractAndReturnResult(data, status, xhr){
         onSuccess(xhr.responseText);
      }

I’m going to put a breakpoint in this function and examine the data, status, and xhr objects that are returned when I run the code. The status object is just a string with the word “success” in it, but data and xhr are much more complex. data is a Document that represents the DOM of the returned XML. xhr has many fields in it, one of which, responseXML, is also a Document. In fact, the debugger tells me that it is the exact same object as data. I can traverse this DOM to get the elements I want. There are native JavaScript ways to do this, but since I’ve already started using jQuery to traverse the web page’s DOM, I’m going to continue to use it to traverse this one.

For example, I can get the element ASIN by searching for element ASIN within Item within Items within the document, using:

         $(data).find("Items Item ASIN")

Actually, though, that will find an array of matching elements which might be empty. To keep it simple at this stage I’m just going to assume that the array has at least one element, and will take the first one as my result. Then I’ll find the text inside that element. That ends up with:

         var asin = $(data).find("Items Item ASIN")[0].textContent;

I’ll change the overall behavior of extractAndReturnResult to pass an object to the success handler instead of just a string, ending up with:

      function extractAndReturnResult(data, status, xhr){
         var result = {
            asin:          $(data).find("Items Item ASIN")[0].textContent,
            author:        $(data).find("Items Item ItemAttributes Author")[0].textContent,
            title:         $(data).find("Items Item ItemAttributes Title")[0].textContent,
            releaseDate:   $(data).find("Items Item ItemAttributes PublicationDate")[0].textContent,
            listPrice:     $(data).find("Items Item ItemAttributes ListPrice FormattedPrice")[0].textContent,
            availability:  $(data).find("Items Item Offers Offer OfferListing Availability")[0].textContent,
            amazonPrice:   $(data).find("Items Item Offers Offer OfferListing Price FormattedPrice")[0].textContent,
            url:           $(data).find("Items Item DetailPageURL")[0].textContent
         };
         onSuccess(result);
      }

Now I have to change the function that gets this result and puts it into the web page, since it’s no longer getting a string. That function used to be an inline function in the main.js file:

                     function(message){
                        message = message.replace(/&/g, "&");
                        message = message.replace(/</g, "&lt;");
                        message = message.replace(/>/g, "&gt;");
                        $("#results").append(message);
                     },

I’m going to change this to refer to a named function called insertResponse, and define that function, shown below:

      function insertResponse(response){
         var html = '<a href="' + response.URL + '">';
         html = html + response.title + '</a> by ' + response.author;
         html = html + ' lists for ' + response.listPrice;
         html = html + ' but sells for ' + response.amazonPrice;
         html = html + '. It was released on ' + response.releaseDate;
         html = html + ' with availability ' + response.availability;
         html = html + '.';
         $("#results").append(html);
      }

It’s verbose, but shows the information. When I look up the same book now, I get a more usable response than before:

JavaScript: The Definitive Guide: Activate Your Web Pages (Definitive Guides) by David Flanagan lists for $49.99 but sells for $31.49. It was released on 2011-05-10 with availability Usually ships in 24 hours.

That’s a good stopping point. There’s still a lot to do before this is a releasable web app. At the very least, I need to check for empty responses and escape any special characters in the data I display. I also want to maintain a list of books, not just look up a single book, and have that list persist between different invocations of this program. So there’s plenty more to come.

Advertisement

3 Comments

  1. […] equivalent, part 2 added basic functionality, part 3 finally called an Amazon web service, and part 4 parsed the web service result. This part will actually reach the point of […]

    Pingback by Chrome Web App Bookshelf – Part 5 « Charles Engelke’s Blog — January 1, 2012 @ 6:22 pm

  2. […] equivalent, part 2 added basic functionality, part 3 finally called an Amazon web service, part 4 parsed the web service result, and part 5 actually was somewhat useful. The series is almost done […]

    Pingback by Chrome Web App Bookshelf – Part 6 « Charles Engelke’s Blog — January 7, 2012 @ 3:53 pm

  3. […] equivalent, part 2 added basic functionality, part 3 finally called an Amazon web service, part 4 parsed the web service result, part 5 was useful enough to publish, and part 6 covered publishing […]

    Pingback by Chrome Web App Bookshelf – Part 7 of 7 « Charles Engelke’s Blog — January 8, 2012 @ 1:37 pm


RSS feed for comments on this post.

Blog at WordPress.com.

%d bloggers like this: