Work with Remote Content

This document describes how to fetch and manipulate remote textual (typically HTML), XML, and JSON data using the makeRequest() function. The makeRequest() function is just one technique for fetching remote data. For an overview of the different approaches you can use, see the Remote Data Requests Developers Guide.

Introduction

For an overview of the different approaches you can use to fetch remote data, see the Remote Data Requests Developers Guide. This chapter focuses on just one approach, which is the gadgets API makeRequest() function.

The gadgets API provides the makeRequest(url, callback, opt_params) function for retrieving and operating on remote web content. It takes the following arguments:

  • String url - The URL where the content is located
  • Function callback - The function to call with the data from the URL once it is fetched
  • Map.<gadgets.io.RequestParameters, Object> opt_params - Additional parameters to pass to the request.

The opt_params argument lets you specify the following:

  • The content type of the request (TEXT, XML,and JSON)
  • The method type of the request (POST or GET)
  • Any headers you want to include in the request
  • The authorization type (NONE, SIGNED, and OAUTH )

Note: You cannot use makeRequest() with type="url" gadgets.

Regardless of the type of data they're fetching, calls to makeRequest() share the same characteristics:

  • Their first parameter is a URL that is used to fetch the remote content.
  • Their second parameter is a callback function that you use to process the returned data.
  • They are asynchronous, meaning that all processing must happen within the callback function. A callback is a function that is passed as a parameter (in the form of a function reference) to another function. Callbacks give third-party developers a "hook" into a running framework to do some processing.
  • They have no return values because they return immediately, and their associated callback functions get called whenever the response returns.

For example, consider the following code snippet, which fetches remote content as text. This code fetches the HTML text of the google.com web page, and displays the first 400 characters:

function getHtml() {    
  var params = {};  
  params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;  
  var url = "http://www.google.com";  
  gadgets.io.makeRequest(url, response, params);
};
function response(obj) {  
  //obj.text contains the text of the page that was requested  
  var str = obj.text;
  var html =  str.substr(0,400);  
  document.getElementById('content_div').innerHTML = html;
};
gadgets.util.registerOnLoadHandler(getHtml);

This example illustrates the basic principles behind how makeRequest() works:

  1. When makeRequest()is called, the gadgets API makes an asynchronous HTTP GET request to the URL passed into the function (in this example, the URL is http://www.google.com).
  2. makeRequest() returns immediately and then calls the callback function later (in this example called response()), when the fetch finishes. This means that you must put any dependent code inside the callback function, or inside functions called by the callback function.
  3. makeRequest() returns a JavaScript object with the following structure:
{
  data : <parsed data, if applicable>,
  errors : <any errors that occurred>,
  text : <raw text of the response>  
}  

This object is supplied as the only argument to the callback function. The callback function performs some operations on the returned data. Typically it extracts portions of the data, combines them with HTML markup, and renders the resulting HTML in the gadget.

Working with Different Content Types

By default, the content of a remote website is returned as text. You can use the opt_params field to set the content type of the returned content to be one of the following:

  • TEXT -- gadgets.io.ContentType.TEXT
  • DOM -- gadgets.io.ContentType.DOM
  • FEED -- gadgets.io.ContentType.FEED
  • JSON -- gadgets.io.ContentType.JSON

Working with Text

Here is an example that fetches data from a CSV (comma-separated value) file and uses it to populate a list of personal contacts. It shows how you set the content type of fetched content in the optional parameters. In the response(obj) callback function, the text value is extracted from obj using obj.text:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Fetch Text Example"/>
  <Content type="html">
  <![CDATA[
  <div id="content_div"></div>
  <script type="text/javascript">

  // This example fetches data from a CSV file containing contact information. In the CSV file, 
  // each record consists of a name, email address, and phone number.
  function getContacts() {
    var params = {};  
    params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;  
    var url = "http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/Contacts.csv";
    gadgets.io.makeRequest(url, response, params);
  };
  // Callback function to process the response
  function response(obj) {               
    var responseText = obj.text;  
    // Set CSS for div.
    var html = "<div style='padding: 5px;background-color: #FFFFBF;font-family:Arial, Helvetica;" 
    + "text-align:left;font-size:90%'>"; 

    // Use the split function to extract substrings separated by comma 
    // delimiters.
    var contacts = responseText.split(",");
    // Process array of extracted substrings.
    for (var i = 0; i < contacts.length ; i++) {
      // Append substrings to html.
      html += contacts[i]; 
      html += " ";

      // Each record consists of 3 components: name, email, and
      // phone number. The gadget displays each record on a single
      // line:
      //
      // Mickey Mouse mickey@disneyland.com 1-800-MYMOUSE
      //
      // Therefore, insert a line break after each (name,email,phone)
      // triplet (i.e., whenever (i+1) is a multiple of 3).
      if((i+1)%3 ==0) { 
        html += "<br>";
      }
    }
    html += "</div>";
    // Output html in div.
    document.getElementById('content_div').innerHTML = html;
  }
  gadgets.util.registerOnLoadHandler(getContacts);

 </script>
  ]]>
  </Content>
</Module>

Working with XML

The Document Object Model (DOM) is an API for navigating HTML and XML documents. You can use makeRequest() to retrieve an XML document as a DOM object. Once you have the object, you can operate on it using standard DOM JavaScript functions. Typically this means extracting the desired data from the XML file, combining it with HTML and CSS markup, and rendering the resulting HTML in your gadget.

With DOM, web content is parsed into a tree of nodes. For example, consider the following snippet of HTML:

<a href="http://www.google.com/">Google's <b>fast</b> home page.</a>

This snippet illustrates the main types of nodes discussed in this section:

  • Element nodes. The element nodes in this snippet are “a” and “b”. Element nodes are the building blocks that define the structure of a document.
  • Text nodes. The text nodes in this snippet are ‘Google’s’, ‘fast’, and ‘home page.’ Text nodes are always contained within element nodes. They are child nodes of the containing element node.
  • Attribute nodes. This snippet has one attribute node: href=’http://www.google.com’. An attribute node provides additional information about its containing element node. However, attributes are not considered child nodes of the element that contains them, which has implications for how you work with them. For more discussion of this topic, see Working With Different Node Types.

This is the DOM structure for the HTML snippet:

DOM tree

To access the data in a DOM object, you “walk the tree,” using DOM functions to navigate parent-child node relationships to get to the data you need.

Example

The following XML file contains data for a series of breakfast items. The top-most parent node is menu, and it has multiple food child nodes. The menu node also contains an attribute node: title="Breakfast Menu". Each food node has name, price, description, and calories child nodes.

The name, price, and calories nodes all contain their own "text" child nodes. Each description node contains a CDATA child node. CDATA is a distinct node type. CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup, such as angle brackets. The only delimiter that is recognized in a CDATA section is the “]]>” string that ends the CDATA section.

<?xml version="1.0" encoding="UTF-8" ?>
<menu title="Breakfast Menu">
  <food>
     <name>Early Bird Breakfast</name> 
     <price>$3.95</price> 
     <description><![CDATA[<div style="color:purple; padding-left:25px;">Two eggs any style with your choice of bacon 
or sausage, toast or English muffin.</div>]]></description> 
     <calories>450</calories> 
  </food>

  <food>
     <name>Chocolate Chip Belgian Waffles</name> 
     <price>$7.95</price> 
     <description><![CDATA[<div style="color:purple; padding-left:25px;">Chocolate chip Belgian waffles covered with 
chocolate syrup and whipped cream.</div>]]></description> 
     <calories>900</calories> 
 </food>

     …
</menu>

The following sample gadget uses this XML file as a data source. It displays a breakfast menu and lets users set a calorie limit. It displays in red any calories that are above the specified limit. Users can also choose whether or not to display descriptions for each breakfast item.

The following code illustrates how to walk the DOM tree to extract data from different node types, and how to combine the data with HTML and CSS markup for display in the breakfast menu gadget.

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Fetch XML" scrolling="true"/>
  <UserPref 
    name="mycalories" 
    display_name="Calorie limit" 
    default_value="800"/>
  <UserPref 
    name="mychoice" 
    display_name="Show Descriptions" 
    datatype="bool" 
    default_value="false"/>
  <Content type="html">
  <![CDATA[
    <div id="content_div"></div>
    <script type="text/javascript">
      // get prefs
      var prefs = new gadgets.Prefs();
      // Calorie limit set by user
      var calorieLimit = prefs.getString("mycalories");
      // Indicates whether to show descriptions in the breakfast menu    
      var description = prefs.getBool("mychoice");

      function makeDOMRequest() {    
        var params = {};  
        params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;  
        var url = "http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/breakfast-data.xml";  
        gadgets.io.makeRequest(url, response, params);
      };
      function response(obj) { 
        // Start building HTML string that will be displayed in <div>.           
        // Set the style for the <div>.        
        var html = "<div style='padding: 5px;background-color: #ccf;font-family:Arial, Helvetica;" 
          + "text-align:left;font-size:90%'>";   
        // Set style for title.
        html +="<div style='text-align:center; font-size: 120%; color: yellow; " 
          + "font-weight: 700;'>"; 
        // obj.data contains a Document DOM element corresponding to the
        // page that was requested
        var domdata = obj.data;

        // Display menu title. Use getElementsByTagName() to retrieve the <menu> element.
        // Since there is only one menu element in the file,
        // you can get to it by accessing the item at index "0". 
        // You can then use getAttribute to get the text associated with the
        // menu "title" attribute.
        var title = domdata.getElementsByTagName("menu").item(0).getAttribute("title");

        // Alternatively, you could retrieve the title by getting the menu element node
        // and calling the "attributes" function on it. This returns an array
        // of the element node's attributes. In this case, there is only one
        // attribute (title), so you could display the value for the attribute at
        // index 0. For example:
        // 
        // var title = domdata.getElementsByTagName("menu").item(0).attributes.item(0).nodeValue; 

        html += title + "</div><br>"; 
        // Get a list of the <food> element nodes in the file
        var itemList = domdata.getElementsByTagName("food");
 
        // Loop through all <food> nodes
        for (var i = 0; i < itemList.length ; i++) { 
        // For each <food> node, get child nodes.
        var nodeList = itemList.item(i).childNodes;

        // Loop through child nodes. Extract data from the text nodes that are
        // the children of the associated name, price, and calories element nodes.
        for (var j = 0; j < nodeList.length ; j++) {
          var node = nodeList.item(j);
          if (node.nodeName == "name") 
          {
            var name = node.firstChild.nodeValue;
          }
          if (node.nodeName == "price") 
          {
            var price = node.firstChild.nodeValue;
          }
          if (node.nodeName == "calories") 
          {
            var calories = node.firstChild.nodeValue; 
          }
          // If the user chose to display descriptions and
          // the child node is "#cdata-section", grab the 
          // contents of the description CDATA for display.
          if (node.nodeName == "description" && description==true)
          {
            if (node.firstChild.nodeName == "#cdata-section") 
              var data = node.firstChild.nodeValue;
          }
        } 
        // Append extracted data to the HTML string.
        html += "<i><b>";
        html += name;
        html += "</b></i><br>";
        html += "&emsp;"; 
        html += price;
        html += " - ";
        // If "calories" is greater than the user-specified calorie limit,
        // display it in red.
        if(calories > calorieLimit) {
          html += "<font color=#ff0000>";
          html += calories + " calories";
          html += " </font>"; 
        }
        else
          html += calories + " calories";
        html += "<br>";
        // If user has chosen to display descriptions
        if (description==true) 
        {
          html += "<i>" + data + "</i><br>";
        } 
      } 
      // Close up div
      html += "</div>";
      document.getElementById('content_div').innerHTML = html;
    };
    gadgets.util.registerOnLoadHandler(makeDOMRequest);
    </script>
  ]]>
  </Content>
</Module>

This code sample illustrates four of the primary functions you use to interact with DOM data:

  • getElementsByTagName(tagname)-- For a DOM document, returns an array of the element nodes whose names match tagname. You can retrieve all of the element nodes in a file by using the wildcard character (*), for example: response.getElementsByTagName("*").
  • getElementById(id)-- For a DOM document, retrieves a single node by id.
  • getAttribute(attrib)-- For an element node, returns the attribute attrib. For example: response.getElementsByTagName("menu").item(0).getAttribute("title").
  • attributes -- For an element node, returns an array of the node’s attributes.

This example only shows a few of the different functions for navigating a DOM tree. Some of the others you might try include lastChild, nextSibling, previousSibling, and parentNode.

Working With Different Node Types

The key to working effectively with DOM is appreciating the sometimes subtle differences between different node types.

Node Type Description Return Values Gotchas
element The structural building blocks of a document, such as <p> , <b> , or <calories>. nodeName: Whatever text is contained inside the angle brackets. For example, the nodeName of <menu> is “menu”.

nodeType: 1

nodeValue : null
An element has a nodeValue of null. To get to the value of a text or attribute node associated with an element, you must go to those nodes. For example: element.firstChild.nodeValue for text, and element.getAttribute(attrib) for attributes.
text Text. A text node is always contained within an element. It is a child of the element. nodeName: #text

nodeType: 3

nodeValue: Whatever text is contained in the node.
Some browsers render all whitespace in a document as text nodes, so that you get “empty” text nodes in your DOM object. This can cause unexpected results when you’re walking the tree. The solution may be as simple as filtering out text nodes that contain only the newline character, or you may want to do more robust handling. For more discussion of this topic, see Whitespace in the DOM.
attribute A key-value pair that provides additional information about an element node (for example, title=”my document”). An attribute is contained by an element node, but it is not a child of the element node. nodeName: The lefthand value in the attribute pair. If the attribute is title=”my document”, the nodeName is title.

nodeType: 2

nodeValue: The righthand value in the attribute pair (in this example, “my document”).
Even though attributes are nodes and are contained within element nodes, they are not child nodes of the element. They inherit from the Node interface, but the DOM doesn't consider them part of the DOM tree. This means that while you can use many of the node functions on attribute nodes (such as nodeName, nodeValue, and nodeType), you cannot access attribute nodes using the DOM tree-walking functions. To access attributes, you use the functions attributes and getAttribute(attrib).
CDATA A section in which content is left alone, not interpreted. CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup. The only delimiter that is recognized in a CDATA section is the "]]>" string that ends the CDATA section. nodeName: #cdata-section

nodeType: 4

nodeValue: Text and markup inside the CDATA delimiters.

The text in the CDATA section has its own markup. This could have implications for how you incorporate it into your gadget.
Other Resources

Working With Feeds

You can add a feed to your iGoogle page by typing its URL into the Add by URL form in the content directory. This uses the gadgets API built-in feed support to create a gadget for the feed and add it to iGoogle. It's easy to use, but it doesn't let you perform any customization to the content or display. Also, you can't use it with other containers.

For more sophisticated feed handling, you can use the makeRequest() method and specify a FEED content type. Feed requests attempt to parse an ATOM or RSS XML feed and return the response as a JSON-encoded object.

With a FEED content type, you can specify the following parameters:

Parameter Data Type Description
gadgets.io.RequestParameters.NUM_ENTRIES Number, optional The number of feed entries to retrieve from the feed. The accepted range is 1 through 100. The default is 3.
gadgets.io.RequestParameters.GET_SUMMARIES Boolean, optional Whether to retrieve the full text summaries for the entries in the feed. This defaults to false. You should only set this to true if you plan to use the data. The full summaries can be quite large and shouldn't be transferred needlessly.

Here are the fields in the JSON feed object:

Field Description
ErrorMsg If defined, describes any error that occurred.
URL The URL of the RSS / Atom feed.
Title The title of the feed.
Description The tagline or description of the feed.
Link Typically, the URL of the feed homepage.
Author The author of the feed.
Entry Array of feed entries. The following fields are nested within the Entry:
  • Title. The title of this feed entry.
  • Link. The URL of this feed entry.
  • Summary. The content or summary of this feed entry.
  • Date. Timestamp for this entry in seconds since Jan 1, 1970. To convert it to the milliseconds needed to initialize a JavaScript Date object with the correct date, multiply by 1000. See the sample gadget code below for an example.

For example, consider this RSS feed:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<rss version="2.0">
<channel>
<title>Code Examples for makeRequest()</title>
<link>http://code.google.com/apis/gadgets/docs/remote-content.html</link>
<description>This feed lists code examples for the gadgets.* makeRequest() function.</description>
<language>en</language>
<item>
<title>Fetch Text Example</title>
<link>http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-csv.xml</link>
<description>This example illustrates how to use makeRequest() with text content.</description>
<author>Google</author>
<pubDate>Sat, 09 Aug 2008 11:46:09 UT</pubDate> </item>
<item>
<title>Fetch XML Example</title>
<link>http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-XML.xml</link>
<description>This section describes how to use makeRequest() with XML content.</description>
<author>Google</author>
<pubDate>Sat, 09 Aug 2008 11:46:09 UT</pubDate>
</item>
<item>
<title>Feed Fetch Example</title>
<link>http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/feed-fetch.xml</link>
<description>This example illustrates how to use makeRequest() with Feed content.</description>
<author>Google</author>
<pubDate>Sat, 09 Aug 2008 10:33:09 UT</pubDate>
</item>
<item>
<title>Fetch JSON Example</title>
<link>http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-json.xml</link>
<description>This example illustrates how to use makeRequest() with JSON content.</description>
<author>Google</author>
<pubDate>Sat, 09 Aug 2008 03:55:28 UT</pubDate> </item>
</channel>
</rss>

If you fetch the above feed with the FEED content type, the returned value is encoded as a JSON string as follows:

{"Entry":
  [
    {
      "Link":"http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-csv.xml",
      "Date":1218282369000,
      "Title":"Fetch Text Example",
      "Summary":"This example illustrates how to use makeRequest() with text content."
    },
    {
      "Link":"http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-XML.xml",
      "Date":1218282369000,
      "Title":"Fetch XML Example",
      "Summary":"This section describes how to use makeRequest() with XML content."
    },
    {
      "Link":"http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/feed-fetch.xml",
      "Date":1218277989000,
      "Title":"Feed Fetch Example",
      "Summary":"This example illustrates how to use makeRequest() with Feed content."
    },
    {
      "Link":"http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/fetch-json.xml",
      "Date":1218254128000,
      "Title":"Fetch JSON Example",
      "Summary":"This example illustrates how to use makeRequest() with JSON content."
    }
  ],
  "Description":"This feed lists code examples for the gadgets.* makeRequest() function.",
  "Link":"http://code.google.com/apis/gadgets/docs/remote-content.html",
  "Author":"Google",
  "URL":"http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/sample-gadgets-feed.xml",
  "Title":"Code Examples for makeRequest()"
}
    

The following example illustrates how to use makeRequest() to fetch a feed and display portions of its data in a gadget. Here is the gadget. It fetches a feed that contains entries from the gadgets developer forum. It lets users specify:

  • The number of entries to show
  • Whether the gadget should display the date for each entry
  • Whether the gadget should display a summary for each entry

This is the code for the example:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Fetch Feed Example" 
    title_url="http://groups.google.com/group/Google-Gadgets-API" 
    scrolling="true">
  </ModulePrefs>
  <UserPref name="show_date" display_name="Show Dates?" datatype="bool" default_value="false"/>
  <UserPref name="num_entries" display_name="Number of Entries:" default_value="5"/>
  <UserPref name="show_summ" display_name="Show Summaries?" datatype="bool" default_value="false"/>
  <Content type="html">
  <![CDATA[
  <style> #content_div { font-size: 80%;  margin: 5px; background-color: #FFFFBF;} </style>
  <div id="content_div"></div>
  <script type="text/javascript">

  // Get userprefs
  var prefs = new gadgets.Prefs();
  var showdate = prefs.getBool("show_date");
  var entries = prefs.getInt("num_entries");
  var summaries = prefs.getBool("show_summ");

  function getFeed() {  
    var params = {};  
    params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.FEED;  
    params[gadgets.io.RequestParameters.NUM_ENTRIES] = new Number(entries);  
    params[gadgets.io.RequestParameters.GET_SUMMARIES] = summaries; 
    var url = "http://groups.google.com/group/Google-Gadgets-API/feed/rss_v2_0_msgs.xml";  
    gadgets.io.makeRequest(url, response, params);
  };

  function response(obj) { 
    // obj.data contains the feed data
    var feed = obj.data;
    var html = "";

    // Display the feed title and description
    html += "<div><b>" + feed.Title + "</b></div>";
    html += "<div>" + feed.Description + "</div><br>";
      
    // Access the data for a given entry
    if (feed.Entry) {
      for (var i = 0; i < feed.Entry.length; i++) {
        html += "<div>"
          + "<a target='_blank' href='" + feed.Entry[i].Link + "'>"
          + feed.Entry[i].Title
          + "</a> ";
        if (showdate==true)
        { 
          // The feed entry Date field contains the timestamp in seconds
          // since Jan. 1, 1970. To convert it to the milliseconds needed
          // to initialize the JavaScript Date object with the correct date, 
          // multiply by 1000.
          var milliseconds = (feed.Entry[i].Date) * 1000; 
          var date = new Date(milliseconds); 
          html += date.toLocaleDateString();
          html += " ";
          html += date.toLocaleTimeString(); 
        }
        if (summaries==true) { 
          html += "<br><i>" + feed.Entry[i].Summary + "</i>";
        }
        html += "</div>";
      }
    }        
    document.getElementById('content_div').innerHTML = html;
  };
  gadgets.util.registerOnLoadHandler(getFeed);
  </script>
  ]]>
  </Content>
</Module>

Working with JSON

JSON (JavaScript Object Notation) is a data interchange format lets you encode certain types of objects (arrays and collections of key-value pairs) as strings that can easily be passed around. You can use the JSON content type to fetch JSON-encoded content as a JavaScript object.

The gadget below fetches content from a text file that contains the following JSON-encoded string:

{"Name" : "Rowan", "Breed" : "Labrador Retriever", "Hobbies" : ["fetching", "swimming", "tugging", "eating"]}

When you fetch content from the text file that contains this string, the return value is a JavaScript object containing key-value pairs (that is, an associative array). This sample retrieves the object and then prints out the key-value pairs it contains. If the value is an array (as indicated by square brackets []), it turns the array contents into a bulleted list:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Fetch JSON Example"/>
  <Content type="html">
  <![CDATA[
    <div id="content_div"></div>
    <script type="text/javascript">

    function makeJSONRequest() {    
      var params = {};
      params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
      // This URL returns a JSON-encoded string that represents a JavaScript object
      var url = "http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/json-data.txt";
      gadgets.io.makeRequest(url, response, params);
    };

    function response(obj) { 
      var jsondata = obj.data;
      var html = "";
      // Process returned JS object as an associative array
      for (var key in jsondata) {
        var value = jsondata[key];
        html += key + ": ";
        // If 'value' is an array, render its contents as a bulleted list
        if (value instanceof Array)
        {
          html += "<br /><ul>";
          for (var i = 0; i < value.length ; i++)
          {
            html += "<li>"+ jsondata.Hobbies[i] + "</li>";
          }
          html+= "</ul>";
        }  
        // If 'value' isn't an array, just write it out as a string
        else {        
          html += value + "<br />";
        }      
      }               
      document.getElementById('content_div').innerHTML = html;
     };
     gadgets.util.registerOnLoadHandler(makeJSONRequest);
     </script>
  ]]>
  </Content>
</Module>

This is the gadget's output:

Name: Rowan
Breed: Labrador Retriever
Hobbies:

  • fetching
  • swimming
  • tugging
  • eating

More about JSON

The gadgets API provides the gadgets.json.stringify() method for encoding object as JSON strings, and the gadgets.json.parse() method for turning a JSON string into an object. Note that because OpenSocial performs automatic HTML escaping of all returned data including application data, you must unescape stringified JSON objects in the appdata store before parsing them, for example: gadgets.util.unescapeString(jsondata).

This example creates a JavaScript array, encodes it as a JSON string, and then converts the JSON string back into an Array object:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="JSON Example" />
  <Content type="html">
  <![CDATA[
     <div id="content_div"></div>
     <script type="text/javascript">
     var html = "";
     // Create Array object
     var myfriends = new Array();
     myfriends[0] = "Touki";
     myfriends[1] = "Rowan";
     myfriends[2] = "Trevor";

     // Encode array as JSON string
     var jsonString = toJSON(myfriends);
     html += "The JSON string is " + jsonString + "<br />";
     html += "The type of jsonString is " + typeof(jsonString) + "<br />";

     // Convert JSON string back to an object
     var arr_obj = toObject(jsonString);
     html += "The type of arr_obj is " + typeof(arr_obj);
     document.getElementById('content_div').innerHTML = html;

     // Encode object as JSON string
     function toJSON(obj) { 
       return gadgets.json.stringify(obj); 
     }

     // Convert JSON string into an object
     function toObject(str) {
       return gadgets.json.parse(str);
    }
    </script>
  ]]>
  </Content>
</Module>

The output from this gadget is as follows:

The JSON string is Touki","Rowan","Trevor
The type of jsonString is string
The type of arr_obj is object

Setting an Authorization Type

The Gadgets API supports the following authorization types:

  • gadgets.io.AuthorizationType.OAUTH (container uses the OAuth protocol)
  • gadgets.io.AuthorizationType.SIGNED (request is signed by the container)
  • gadgets.io.AuthorizationType.NONE (the default)

How you use these methods depends on your container. Here is one example of using a signed authorization type that is specific to Orkut. For a discussion of using OAuth in gadgets, see Writing OAuth Gadgets.

Methods

makeRequest() allows you to choose between the HTTP GET and POST methods.

GET

Typically, you use GET to retrieve information from a website. GET is the default mode for makeRequest(), but you can explicitly make a GET request as follows:

function makeGETRequest(url) {
   var params = {};
   params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
   gadgets.io.makeRequest(url, response, params); 
};
function response(obj) {
   alert(obj.text); 
};
makeGETRequest("http://example.com"); 

To pass parameters to your server in the GET request, just append them to the querystring when you make the request:

makeGETRequest("http://example.com?param1=12345&param2=hello"); 

POST

POST requests are typically used to pass data to a server with the intent to modify or delete records. You can pass larger amounts of data in a POST request than you would be normally able to with a GET request.

A POST can be performed using the following code:

function makePOSTRequest(url, postdata) {
   var params = {};
   postdata = gadgets.io.encodeValues(postdata);
   params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
   params[gadgets.io.RequestParameters.POST_DATA]= postdata;
   gadgets.io.makeRequest(url, response, params); 
};
function response(obj) {
   output(obj.text); 
};
var data = {   data1 : "test",   data2 : 123456 }; 
makePOSTRequest("http://example.com", data); 

In addition to specifying the METHOD in the opt_params for this request, you should specify a parameter under the key gadgets.io.RequestParameters.POST_DATA. The default encoding for POST is application/x-www-form-urlencoded which means that the value for the POST_DATA parameter should be a series of urlencoded key/value pairs joined with ampersands (&). To make converting objects of data into this format easier, the function gadgets.io.encodeValues is provided. encodeValues accepts a JavaScript object and returns an encoded string suitable for the POST_DATA parameter.

For example, running:

var data = {   data1 : "test",   data2 : 123456 };  gadgets.io.encodeValues(data); 

produces the string:

data1=test&data2=123456 

This string can be passed directly as the value for gadgets.io.RequestParameters.POST_DATA.

Refreshing the Cache

If you are using makeRequest() to fetch content that is updated more than once an hour, such as feed data, you might not get the latest updates. This is because your results are cached to make your gadget run faster. If you want to be sure that your gadget has the latest data, you can use the refreshInterval parameter to bypass the cache and force a refresh to happen within the interval you specify. In other words, the cache is refreshed every X seconds, where X = refreshInterval.

Calls to makeRequest are cached by default. In the example below, the wrapper function takes the same parameters as the makeRequest call, but accepts another parameter named refreshInterval, which allows you to specify the cache duration.

function makeCachedRequest(url, callback, params, refreshInterval) {
  var ts = new Date().getTime();
  var sep = "?";
  if (refreshInterval && refreshInterval > 0) {
    ts = Math.floor(ts / (refreshInterval * 1000));
  }
  if (url.indexOf("?") > -1) {
    sep = "&";
  }
  url = [ url, sep, "nocache=", ts ].join("");
  gadgets.io.makeRequest(url, callback, params);
}

Caching serves a useful purpose, and you should be careful not to refresh the cache so often that you degrade performance. Caching makes fetching data faster. It also reduces the load on third-party servers hosting the remote content. You should try to avoid disabling the cache completely (which you would do by using refreshInterval: 0). If your gadget is getting millions of page views a day, sending out millions of requests to these servers, turning off caching could not only adversely affect your gadget's performance, but it could overload the servers that provide your gadget with data.

Since content is refreshed by default every hour, it only makes sense to specify an interval less than an hour. The recommended range is for refreshInterval is more than 60, and less than 3600.

Back to top