SYSK 217: Processing WebService Response using JSON

For those of you who haven’t done much work with JSON, this blog may be a good introduction...

 

JSON stands for JavaScript Object Notation (pronounced ‘Jason’) and is a lightweight data-interchange format with smaller footprint than xml, human readable (just like xml), simple language model supported by the following languages: ActionScript, C, C#, ColdFusion, E, Java, JavaScript, Lua, ML, Objective CAML, Perl, PHP, Python, Rebol, and Ruby.

 

The popularity of JSON is largely due to its simplicity, more compact footprint (compared to xml) and the fact that JSON can be accessed across domains by dynamically creating script tags, which is handy for mash-ups.

 

The rules are very simple (http://www.json.org/):

1. Objects start with { and end it with }

2. Object members (properties) are contained within {}

3. Members are represented by two strings – name and value separated by :

4. A value can be a string, number, object, array, true, false, null

5. To create an array of objects, use []

 

Here is a JSON string example:

{

            "books":

            [

                        {"book":

                                    {

                                                "title":"The Elegant Universe",

                                    "author":"Brian Greene",

                                    "ISBN":"0-375-70811-1"

                                    }

                        },

                        {"book":

                                    {

                                                "title":"Nanotechnology: A Gentle Introduction to the Next Big Idea",

                                                "author":"Mark Ratner and Danial Ratner",

                                                "ISBN":"0-13-101400-5"

                                    }

                        },

                        {"book":

                                    {

                                                "title":"A Short History of Nearly Everything",

                                                "author":"Bill Bryson",

            "ISBN":"0-7679-0817-1"

                                    }

                        }

            ]

}

 

JSON’s eval() function parses the JSON string returns a javascript object, and it is quite fast.

 

NOTE: The eval() function can be unsafe as there is no guaranty that the string doesn’t contain some functions that, execute by the browser, could cause damage. See http://www.json.org/js.html. To keep the sample easy to follow, I’m omitting handling this problem.

 

Now, let’s create a web service in C# 2.0 with the following web method:

 

    [WebMethod]

    public string GetGreatBooks()

    {

        return "{\"books\":[{\"book\":" +

            "{" +

            "\"title\":\"The Elegant Universe\"," +

            "\"author\":\"Brian Greene\"," +

            "\"ISBN\":\"0-375-70811-1\"" +

            "}" +

      "}," +

      "{\"book\":" +

            "{" +

            "\"title\":\"Nanotechnology: A Gentle Introduction to the Next Big Idea\"," +

            "\"author\":\"Mark Ratner and Danial Ratner\"," +

            "\"ISBN\":\"0-13-101400-5\"" +

            "}" +

      "}," +

      "{\"book\":" +

            "{" +

            "\"title\":\"A Short History of Nearly Everything\"," +

            "\"author\":\"Bill Bryson\"," +

            "\"ISBN\":\"0-7679-0817-1\"" +

            "}" +

      "}" +

        "]}";

    }

 

On the HTML page (code below), we have a button which, when clicked would invoke a web service using XmlHttpRequest. Once the web service returns the JSON string above, the following code is executed:

var myOnComplete = function(responseText, responseXML)

      {

         // getText strips off the xml stuff added by web method

         // and returns just JSON string

         var r = eval('(' + getText(responseText) + ')');

         var e = document.getElementById('GreatBooks');

              

         e.innerHTML = "<h3>Great Books</h3>";

                        

         for (var i = 0; i < r.books.length; i++)

      {

      e.innerHTML += (i+1) + "). <b>" + r.books[i].book.title +

            "</b> by " + r.books[i].book.author +

            " (ISBN " + r.books[i].book.ISBN + ")<br>";

      }

      }

 

 

Here is the complete source for the web page:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head>

    <title>Test Page</title>

    <script type="text/javascript">

    {

        // TODO: In production apps, replace alert with less invasive method

        function WebServiceProvider()

        {

            var me = this;

            var request = null;

            var prevUrl = "";

        var inProgress = false;

      var isComplete = false;

     

            // call this function to call a web service

      this.invokeService = function(url)

      {

            if (IsBusy())

            {

                  alert("Busy processing another request " + prevUrl);

            }

    else

    {

            prevUrl = url;

            inProgress = true;

            isComplete = false;

           

      if (window.XMLHttpRequest)

      {

      // XMLHttpRequest object supported by browser

      request = new XMLHttpRequest();

      request.onreadystatechange = onRequestComplete;

      request.open("GET", url, true);

        request.send(null);

      }

      else

      {

      // IE uses an ActiveX control as XMLHTTP implementation

      if (window.ActiveXObject)

      {

            request = new ActiveXObject("Microsoft.XMLHTTP");

            if (request != null)

            {

            request.onreadystatechange = onRequestComplete;

            request.open("GET", url, true);

                  request.setrequestheader("Pragma","no-cache");

            request.setrequestheader("Cache-control","no-cache");

            request.send();

            }

            else

            {

                              alert("Unable to create Microsoft.XMLHTTP");

            }

            }

            else

            {

                        alert("Browser does not support XML HTTP Request");

            }

      }

      }

            } // end invokeService

   

      // Only one request at a time allowed

      var IsBusy = function()

      {

            return inProgress && !isComplete;

      }

   

    var onRequestComplete = function()

    {

            var STATE_COMPLETED = 4;

            var STATUS_200 = 200;

       

        if (request != null)

        {

            if (request.readyState == STATE_COMPLETED)

            {

                  inProgress = false;

                  isComplete = true;

           

      if (request.status == STATUS_200)

                  {

                        if (me.onComplete)

                              me.onComplete(request.responseText, request.responseXML);

                  }

                  else

                  {

                        alert("Error " + request.status + " invoking " + prevUrl);

                  }

            }

            }

      }

     

      // A way for callers of this function to override this method

      var onComplete = null;

      this.onComplete = function(responseText, responseXML)

      {

      }

     

      } // end function WebServiceProvider

   

        function CallWS()

        {

            // strip off xml stuff returned by a web method

            var getText = function(xml)

            {

                var xmlDoc;

               

      if (document.implementation && document.implementation.createDocument)

            xmlDoc = document.implementation.createDocument("", "", null);

      else if (window.ActiveXObject)

            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");

      else

      {

            alert('Browser does not support XML documents');

            return;

      }

     

      xmlDoc.async = false;

      xmlDoc.loadXML(xml);

     

      return xmlDoc.documentElement.text;

            }

           

            // XMLHttpRequest completion function

            var myOnComplete = function(responseText, responseXML)

            {

                var r = eval('(' + getText(responseText) + ')');

                var e = document.getElementById('GreatBooks');

               

                e.innerHTML = "<h3>Great Books</h3>";

                        

                for (var i = 0; i < r.books.length; i++)

      {

            e.innerHTML += (i+1) + "). <b>" + r.books[i].book.title +

            "</b> by " + r.books[i].book.author +

            " (ISBN " + r.books[i].book.ISBN + ")<br>";

      }

            }

           

            var provider = new WebServiceProvider();

            provider.onComplete = myOnComplete;

   

            provider.invokeService("http://localhost:2288/WebSite1/BookService.asmx/GetGreatBooks");

        }

    }

    </script>

</head>

<body style="font-size: 0.8em; font-family:Verdana;">

    <input type="button" value="Click here to get names of great books..." onclick="javascript:CallWS();" />

    <div id="GreatBooks"></div>

</body>

</html>