Archive for December, 2005

JSON re-loaded – Travel API lauched

Tuesday, December 20th, 2005

On Thursday Yahoo! launched JSON support for Search and the Maps API’s and today Travel went live with two new calls that support JSON as well. The new services return detailed information about a trip created with the Yahoo! Travel Trip Planner.

I put together a little page that uses the two new calls with the JSON output to display trip details.

jsonTravel

spiffYSearch

Tuesday, December 20th, 2005

Kent Brewster wrote a really cool app that uses Y! Search JSON and can be included in any web site to display search results dynamically in that page.

spiffYSearch

WordPress on Yahoo

Tuesday, December 20th, 2005

After Yahoo! annouced that they will offer Moveable Type as the default blogging tool, WordPress just made the followig announcement: “Well today we’re very excited to announce we’re adding a new host to the page with a familiar name – Yahoo! ” Read the full post.

Web Services + JSON = Dump Your Proxy

Thursday, December 15th, 2005

In my post on "How to build a Maps Mash-up" I mentioned that there are different ways to overcome the browser security restrictions to retrieve data from another domain (cross-domain restriction). The previous sample used the software proxy method to make the Web service requests and this post talks about a way to make a request without a proxy. It’s the dynamic script tag method.

Today Yahoo! added a new output option for part of their Web services called JSON. This makes it possible to make the JavaScript WS request without using the XMLHTTPRequest object. It is a great way to pull data from another domain because you can dump your proxy and all the data will not route through your server anymore. I will talk about the pros and cons of both these approaches later, but first I want to give an overview of what JSON is, how it works and show some sample code.

What is JSON?

On Doug Crockford’s page it reads like that: "JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate." And that’s how it look like:

{"ResultSet":{"Result":[{"precision":"city","Latitude":"34.05217","Longitude":"-118.243469","Address":"","City":"LOS ANGELES","State":"CA","Zip":"","Country":"US"}]}}}

The string above is returned by Y! Geocoder for the query “LA”. JSON is a serialized JavaScript object, which JavaScript can turn back into an object. For Yahoo! WS the structure of the JSON string is similar to the XML result but the difference between and attribute and element can’t be made. The following is a comparison of the XML result for the same call.

<ResultSet ... >
  <Result precision="city">
    <Latitude>34.05217</Latitude>
    <Longitude>-118.243469</Longitude>
    <Address></Address>
    <City>LOS ANGELES</City>
    <State>CA</State>
    <Zip></Zip>
    <Country>US</Country>
  </Result>
</ResultSet>

One way to get from JSON to a JavaScript object is to call eval(), with the string as argument. The following sample uses the Geocoder result to display LA’s Latitude and Longitude in an alert box. This is just static.

eval.html
<HTML>
  <BODY>
    <script language"javascript">
      var location = eval({"ResultSet":{"Result":[{"precision":"city","Latitude":"34.05217","Longitude":"-118.243469","Address":"","City":"LOS ANGELES","State":"CA","Zip":"","Country":"US"}]}});
      alert("Lat:" + location.ResultSet.Result[0].Latitude + " Lon: " + location.ResultSet.Result[0].Longitude );
    </script>
  </Body>
</HTML>

This is nice but doesn’t do too much in the real world. The problem was to get the data from a Web service that is located on another domain imported without using a proxy.

The secret sauce

Adding the <Script> tag dynamically in the DOM tree of the browser is the answer and the JSON response helps to get the data in a format that is easy to digest for JavaScript. When a Script tags gets dynamically added to the DOM tree the code (script URL) gets executed on the fly. The trick is that instead pointing to a JavaScript library, we include a Web service request in the tag that returns data in the above mentioned format. The Yahoo! Web services that offer the JSON output option also supports a parameter called ‘callback’ and all it does is wrap the return data in a function with the name of the callback value. http://api.local.yahoo.com/MapsService/V1/geocode?appid=dantheurer&location=la&output=json&callback=getLocation would result in something like this getLocation({"ResultSet":{"Result":[{"precision":"city",….) which tries to call the getLocation function (callback) that needs to be implemented to deal with the data.

Below is a sample that takes a location as an input parameter, then calls the Y! Geocoder WS and displays Long / Lat in the page.

geocodeJson.html
<script type="text/javascript" src="jsr_class.js"></script>
<script type="text/javascript">
  //<![CDATA[
  var appid = "dantheurer";

  //That is the callback function that is specified in the request url and gets executed after the data is returned
  function getLocation(jData) {
    if (jData == null) {
      alert("There was a problem parsing search results.");
      return;
    }
    //get the values out of the object
    var lat = jData.ResultSet.Result[0].Latitude;
    var lon = jData.ResultSet.Result[0].Longitude;
    //build some html
    var smart = "Long: " + lon + "<br />" + "Lat: " + lat;
    //add it in the page DOM
    document.getElementById('result').innerHTML = smart;
  }

  function addGeocode() {
    var location = document.getElementById("geoquery").value;
    // Build the Yahoo! web services call
    request = 'http://api.local.yahoo.com/MapsService/V1/geocode?appid=' + appid + '&location=' + location + '&output=json&callback=getLocation';
    // Create a new script object
    aObj = new JSONscriptRequest(request);
    // Build the script tag
    aObj.buildScriptTag();
    // Execute (add) the script tag
    aObj.addScriptTag();
  }
  //]]>
</script>

Jason, my cube neighbor, wrote a really nice class that deals with the dirty work of adding, removing and making sure the tags are unique. If you open up the file, it even has a security warning from the inventor of JSON in there. Below is the code snippet:

jsr_class.js

function JSONscriptRequest(fullUrl) {
  // REST request path
  this.fullUrl = fullUrl;
  // Keep IE from caching requests
  this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
  // Get the DOM location to put the script tag
  this.headLoc = document.getElementsByTagName("head").item(0);
  // Generate a unique script tag id
  this.scriptId = 'YJscriptId' + JSONscriptRequest.scriptCounter++;
}

// Static script ID counter
JSONscriptRequest.scriptCounter = 1;

// buildScriptTag method
JSONscriptRequest.prototype.buildScriptTag = function () {
  // Create the script tag
  this.scriptObj = document.createElement("script");

  // Add script object attributes
  this.scriptObj.setAttribute("type", "text/javascript");
  this.scriptObj.setAttribute("src", this.fullUrl + this.noCacheIE);
  this.scriptObj.setAttribute("id", this.scriptId);
}

// removeScriptTag method
JSONscriptRequest.prototype.removeScriptTag = function () {
  // Destroy the script tag
  this.headLoc.removeChild(this.scriptObj);
}

// addScriptTag method
JSONscriptRequest.prototype.addScriptTag = function () {
  // Create the script tag
  this.headLoc.appendChild(this.scriptObj);
}

Here is what the script does in some bullet points

  • Build request URL with input parameter and callback function
  • Build the script tag that contains the request URL
  • Add the new tag to the DOM tree
  • As soon as the tag gets addes, the WS request gets executed and what gets returned is the JSON response wrapped in a function call. The name of the function got specified in the callback parameter.
  • The response, which is a function call now calls the matching function with the JSON data as parameter. This is where the data can get extracted.
  • The script tag gets removed again

The sample I wrote for the Maps launch depends on a PHP proxy. I took that sample and wrote a version that uses JSON instead. Maybe it’s because it’s new, but for some reason I like the new version better.

jsonSample

All this is not just a hack that might not work again tomorrow because of a browser upgrade. Most of the dynamic ads use the dynamic script tag to display themselfes depending on the context. There are of course ups and downs for both technologies and not everyone agrees, but here are some points to think about.

Proxy method

  • More robust, error handling is easier
  • More control on the server side
  • It has some security implications on the server side as the proxy could be abused.
  • The server side can have additional functionality implemented that is hidden to the caller e.g. login, exchange secrets…

Dynamic script tag

  • No XML parsing necessary
  • Performance win
  • No traffic gets routed (and counted) on your end.
  • JSON converters don’t know that they should define an array if they is only one nested element in a tag, even if the Schema allows 1..n
  • More cross-browser issues
  • Positive impact on rate limiting if it’s done per IP
  • No need to set up a proxy

For more information about JSON, JavaScript and Web services have a look at our brand new Yahoo! Developer Network JavaScript Developer Center.