Chapter 35. JSON

In Lessons 33 and 34, you sent a request to the server and received data that you did something with; in Lesson 33, you retrieved a list of names separated by commas and parsed that data to make a functional application. Lesson 34 had you make a POST request to the server, which responded with text that you displayed in an alert box. The data returned from the server in those two lessons was simple, and you will rarely deal with such a simplistic set of data in a real-world application. So this lesson introduces you to an interchange format suitable for representing complex data. It is today's standard in Ajax communication: JavaScript Object Notation (JSON). First, let's take a look at how JSON came about.

XML RULES THE WORLD...OR DOES IT?

Since its creation, eXtensible Markup Language (XML) has steadily gained ground as the data interchange format of choice. The ability to mark up data and exchange it between different computer systems made XML the de facto standard for data transmission. Don't worry if you're not familiar with XML. The basics of the language are very simple. In fact, it looks a lot like HTML. Following is a simple XML document that describes a person:

<?xml version="1.0" encoding="utf-8"?>

<person>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <address>
        <street>1234 XYZ</street>
        <city>Dallas</city>
        <state>Texas</state>
        <country>USA</country>
    </address>
    <phoneNumbers>
        <phoneNumber type="home">111-123-4567</phoneNumber>
        <phoneNumber type="cell">111-234-5678</phoneNumber>
</phoneNumbers>
    <company><![CDATA[John & Joe's Lobster House]]></company>
</person>

XML has a stricter rule set than HTML:

  • The first line is called the XML declaration, and every XML document must have one.

  • Every XML document must also have one, and only one, document element, the root element of the document. In this document, <person/> is the document element (the <html/> element is the document element in HTML documents).

  • Every beginning tag must have a closing tag, and all elements should be properly nested.

  • Special characters such as ampersands (&) and angle brackets cannot be used in data unless they are inside a CDATA (character data) block.

XML's strict rule set and the ability to create your own formats make it a popular choice for sharing text-based data such as RSS feeds. XML is used everywhere. In fact, you'd be hard pressed to find a web service or a programming interface running on a remote server that doesn't send and receive data in XML.

Naturally, the use of XML as an interchange format trickled into browser/server communication. It seemed perfect for the browser because XML documents could be loaded into a DOM, and developers could traverse the XML document's tree to get the data they needed. As Ajax gained in popularity, XML was the de facto format for exchanging data (remember, the x in Ajax stands for XML).

While XML is great for sending and receiving data between networks and computer systems, there are some problems when it comes to using it as a data interchange format with the browser. First, XML documents are inherently verbose because XML is a markup language. Refer back to the sample XML document earlier in this section. Every XML document has an XML declaration at the top of the file, and each element containing data must have a beginning and ending tag. In addition, data containing XML's special/reserved characters has to be represented as character data, as shown in the <company/> element in this XML document.

The verbose nature of XML makes this simple document weigh in with a size of 507 bytes. Admittedly, 507 bytes isn't very large, but imagine a document describing over 50 people. The file could get very large fast. File sizes are directly linked to a web application's performance. You want to squeeze as much performance out of your application as possible, and smaller file sizes can help with that.

The second problem with XML is the DOM API. The DOM was developed as a means of programmatically representing structured documents, like HTML and XML. In order for developers to work with an XML document in JavaScript, the document is loaded into its own DOM object (independent from the HTML page's DOM object). JavaScript developers work with DOMs all the time, but the API is cumbersome and unfriendly. For example, the code to directly access the <country/> element's value (in the <address/> element) looks like this in IE:

doc.documentElement.childNodes[2].childNodes[3].nodeValue

This code uses doc as the top-level DOM object (much as document is the top-level DOM object of HTML pages), documentElement gets the <person/> element, childNodes[2] gets the <address/> element, childNodes[3] accesses the <country/> element, and the nodeValue property gets the value contained within the element. That's a lot of code to get to one element's value, and it's cryptic, making maintenance difficult.

Note

The previous code listing is actually incorrect if the XML document is loaded into a standard XML DOM object. Code for accessing the <country/> element in standard XML DOM objects are actually more complicated.

So XML is inefficient for data interchange for JavaScript developers. What, then, is the alternative?

JAVASCRIPT OBJECT NOTATION SAVES THE WORLD

Douglas Crockford, a computer scientist, recognized XML's inefficiencies for JavaScript developers and worked to develop an interchange format that would result in smaller files and make working with that data easier. What he developed was a lightweight format dubbed JavaScript Object Notation, or JSON (pronounced Jason) for short.

JSON is based on a subset of the JavaScript language, and the idea behind it is that you can adequately describe data by using object and array literals. For example, the XML document in the previous section can be converted into a JavaScript object like this:

var person = {
    firstName : "John",
    lastName : "Doe",
    address : {
        street : "1234 XYZ",
        city : "Dallas",
        state : "Texas",
        country : "USA"
    },
    phoneNumbers : [
        {
            type : "home",
            number : "111-123-4567"
        },
        {
            type : "cell",
            number : "111-234-5678"
        }
    ],
    company : "John & Joe's Lobster House"
};

This code uses object and array literals to describe the same data as the XML in the previous section. The object is called person, and fulfills the same purpose as the <person/> root element in the XML file. The <firstName/>, <lastName/>, and <address/> elements are converted to properties of the person object. Next, <phoneNumbers/> is converted to an array literal, and each item in the array is an object representing a <phoneNumber/> element.

Now, this is a JavaScript object, not JSON. The primary difference between JavaScript object/array literals and JSON is that JSON does not have variables, properties, methods, or any other construct of the JavaScript language. JSON is a text format meant to represent data only, so the JSON equivalent of this JavaScript code is the following:

{
    "firstName" : "John",
    "lastName" : "Doe",
    "address" : {
        "street" : "1234 XYZ",
        "city" : "Dallas",
        "state" : "Texas",
        "country" : "USA"
    },
    "phoneNumbers" : [
        {
            "type" : "home",
            "number" : "111-123-4567"
        },
        {
            "type" : "cell",
            "number" : "111-234-5678"
        }
    ],
    "company" : "John & Joe's Lobster House"
}

This is called a JSON structure. Notice that the property names become string values: JSON is serialized JavaScript objects and arrays. Serialization is the process of converting objects into text, and serialization (along with deserialization—converting text into objects) is what makes JSON a suitable transmission format for JavaScript developers. JSON is advantageous for JavaScript developers for two reasons:

  • JSON data is smaller than XML data, and it gets even smaller when you remove all unnecessary whitespace. The JSON data in this example is 462 bytes, and you can shrink that to 256 bytes. Even when compressed into one line, the XML equivalent is still larger at 399 bytes.

  • Deserializing JSON means you get a JavaScript object that you can use in your program. So after you deserialize the previous JSON and assign the resulting object to a variable called person, you can easily find out what country John Doe lives in by using the following code:

    var country = person.address.country;

These two reasons, size and ease of use, make JSON perfect for JavaScript developers. So let's look at how you can serialize to and deserialize from JSON.

CONVERTING TO AND FROM JSON

Since JSON usurped XML as the de facto standard for Ajax data transmission, the latest version of the JavaScript language has had built-in JSON support. A new built-in object, called JSON, exists in the following browsers:

  • Internet Explorer 8+ (only in IE8 mode)

  • Firefox 3.5+

  • Chrome 4+

  • Safari 5+

JSON support doesn't end with these browsers, however. Before JSON support was built into JavaScript, Crockford wrote a JavaScript script that can be used in any browser. You can find this script at www.json.org/json2.js or in the code download accompanying this book at www.wrox.com. This utility creates a JSON object in browsers that do not support the native JSON object, and its functionality exactly mirrors that of the native JSON object. So until all older browsers are phased out, it's a good idea to include the json2.js file in any page in which you plan on using JSON.

The JSON object has two methods:

  • parse(): Deserializes JSON to create a JavaScript object or array.

  • stringify(): Serializes a JavaScript object or array into JSON.

First let's look at the stringify() method. It accepts a JavaScript value as an argument and returns a string containing the JSON text. To get an idea of how stringify() works, let's imagine that you work at a car dealership, and that one of your duties is to write a web application that allows visitors to search for vehicles based on make, model, and year. Part of your code creates a data type called Car. Following is its definition:

function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
}

The constructor has three parameters: make, model, and year. It uses these values to populate the make, model, and year properties that Car objects will have. Now let's simulate a user's input by creating a Car object with some data and passing it to the JSON.stringify() method, like this:

var toyotaCamry = new Car("Toyota", "Camry", 2002);

var carJson = JSON.stringify(toyotaCamry);

The first line of this code creates a Car object representing a 2002 Toyota Camry and assigns it to the toyotaCamry variable. In the next line the toyotaCamry object is passed to the JSON.stringify() method, and the resulting string is returned to the carJson variable. This gets you the same result as doing the following:

var carJson = '{"make":"Toyota","model":"Camry","year":2002}';

Notice that the number 2002 is not a string. By design, the JSON.stringify() method does not serialize numeric or Boolean values, in order to preserve their value.

Once you have your data in JSON form, you can do whatever you want with it. More than likely, though, you serialized an object to send it to the server, and you can do so by adding the following method to the ajaxUtility object:

var ajaxUtility = {
    // other methods snipped for printing

    makePostRequest : function(url, data, callback) {
        var xhr = this.createXHR();

        xhr.open("POST", url);

        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                var status = xhr.status;
                if ((status >= 200 && status < 300) || status === 304) {
                    callback(xhr.responseText);
                } else {
                    alert("An error occurred");
                }
            }
        };

        xhr.send(data);
    }
};

Unlike POSTing data with the ajaxUtility.postFromForm() method you wrote in the last lesson, POSTing JSON doesn't require the application/x-www-form-urlencoded Content-Type header. So to send the JSON in carJson with this new method, all you need to do is this:

ajaxUtility.makePostRequest(serverUrl, carJson, processResponse);

While there are times when you will want to send data to the server using JSON because of the data's complexity, the simple data in this example would be better sent using the ajaxUtility.postFromForm() method.

Let's imagine that you sent the car query in a request to a server application that determines if the specified car is in stock. The server processes the request and responds with the following JSON:

{
    "searchedCar" : {
        "make" : "Toyota",
        "model" : "Camry",
        "year" : 2002
    },
"isFound" : false,
    "results" : [
        {
            "make" : "Toyota",
            "model" : "Camry",
            "year" : 2005
        },
        {
            "make" : "Honda",
            "model" : "Accord",
            "year" : 2002
        }
    ]
}

This JSON structure has three primary pieces of data. First it contains the searchedCar object structure that contains the make, model, and year that were searched for. Next it contains a structure called isFound containing a Boolean value determining whether or not the searched-for car was found. Last is a structure, called results, that contains a list of cars that the user might find suitable.

Of course, the data in this format isn't overly helpful to you, so you want to deserialize it using the JSON.parse() method. Now, your request was sent via the ajaxUtility, so the following function was executed when the request completed successfully:

function processResponse(data) {
    var results = JSON.parse(data);

    // more code here
}

In the processResponse() function, the first line calls the JSON.parse() method, passing it the value of the data parameter, and assigns the resulting object to the results variable. Now you have an actual JavaScript object than you can use in your code, so you can easily provide the user with information regarding his or her query, like this:

function processResponse(data) {
    var results = JSON.parse(data);

    if (results.isFound) {
        // do something with the matched data
    } else {
        // do something to let the user know no match was found
        // do something with the suggestions
    }
}

By parsing JSON into a JavaScript object you gain immediate access to the data the JSON structure contained in your JavaScript code, and you don't have to traverse a DOM to get it.

The ease of transforming JSON into JavaScript makes JSON the most popular means of transporting data to and from a JavaScript application. Let's expand on this car dealership scenario in the next section.

TRY IT

In this lesson, you learn how to leverage JSON to send, receive, and work with complex data in JavaScript.

Lesson Requirements

For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users, Notepad is available by default on your system or you can download Microsoft's free Visual Web Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/). Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda (www.panic.com/coda/). Linux users can use the built-in VIM.

You also need a modern web browser. Choose any of the following:

  • Internet Explorer 8+

  • Google Chrome

  • Firefox 3.5+

  • Apple Safari 4+

  • Opera 10+

Additionally, you need webserver software and PHP installed on your computer. Refer to Lesson 31 on the DVD for installation instructions. Create a subfolder called Lesson35 in the root directory of your webserver, and copy the eventUtility.js and ajaxUtility.js files into it. Also copy the car_dealership.php and json2.js files from the code download and paste them in the Lesson35 folder. Store the files you create in this lesson in the Lesson35 folder.

The car_dealership.php file responds with one JSON structure. The server's response is as follows:

{
    "searchedCar" : {
        "make" : "value",
        "model" : "value",
        "year" : numericValue
    },
    "isFound" : booleanValue,
    "results" : [
        {
            "make" : "value",
            "model" : "value",
            "year" : numericValue
        },
        // more object structures if necessary
    ]
}

This JSON structure defines data containing the search query, a Boolean value indicating whether or not a match was found, and an array structure called results containing one or more car-like object structures, each having make, model, and year information.

Step-by-Step

  1. Open your text editor, and then type the following:

    <html>
    <head>
        <title>Lesson 35: Example 01</title>
    </head>
    <body>
    <div id="divResults">
        <div id="divSearchMessage"></div>
        <div id="divSearchResults"></div>
    </div>
    <form name="theForm" method="post" action="car_dealership.php"
        onsubmit="submitForm(event);">
        <p>
            Make: <input type="text" name="txtMake" value="" />
        </p>
        <p>
            Model: <input type="text" name="txtModel" value="" />
        </p>
        <p>
            Year: <input type="text" name="txtYear" value="" />
        </p>
        <p>
            <input type="submit" name="btnSubmit" value="Submit" />
        </p>
    </form>
    <script type="text/javascript" src="eventutility.js"></script>
    <script type="text/javascript" src="ajaxUtility.js"></script>
    <script type="text/javascript" src="json2.js"></script>
    <script type="text/javascript">
    
    function submitForm(event) {
        var data = ajaxUtility.getRequestBody(document.theForm);
    
        ajaxUtility.makeGetRequest("car_dealership.php?" + data,
             processResponse);
    
        eventUtility.preventDefault(event);
    }
    
    function getCarString(carObj) {
        return [carObj.year, carObj.make, carObj.model].join(" ");
    }
    
    function processResponse(data) {
    
    }
    
    
    </script>
    </body>
    </html>

    Save it as lesson35_example01.htm. At the top of the body is a <div/> element with an id of divResults. Inside this element are two more <div/> elements. The one with the id of divSearchMessage will contain a message to the user telling him or her whether or not the query garnered any results. The <div/> element with an id of divSearchResults will contain the results/suggestions.

    Next is a form that that contains <input/> elements for the make, model, and year to search for. It has an onsubmit event handler that calls the submitForm() function. This function uses the ajaxUtility.getRequestBody() method to format the data into a URL's query string, and then it makes a GET request to the PHP file along with the query string contained within the data.

    Another function, called getCarString(), is defined. It accepts a car-like object as an argument and formats the object into a "year make model" string, like "2002 Toyota Camry."

    The third function is called processResponse(), and it's the callback function of the Ajax request made within submitForm().

  2. Add the bold lines in the following code to the processResponse() function:

    function processResponse(data) {
        var result = JSON.parse(data);
        var messageDiv = document.getElementById("divSearchMessage");
        var message = "";
        var length = result.results.length;
        var carStr = getCarString(result.searchedCar);
    
        if (result.isFound) {
            message = "We found " + result.results.length + " matches for " + carStr;
        } else {
            message = "We could not find " + carStr + ". You might like: ";
        }
    
        messageDiv.innerHTML = message;
    }

    This new code parses the JSON data and assigns the resulting object to the result variable, and a reference to the <div/> element with an id of divSearchMessage is retrieved from the document and assigned to the messageDiv variable. Another variable, called message, is set to an empty string. It will contain a message to tell the user how the search went. Then a variable called length is assigned the length of the result.results array. Finally, carStr is assigned the return value obtained by calling getCarString() with the result.searchedCar property passed as an argument.

    Next some code using the result object's isFound property determines if a match was found, and an appropriate message is constructed and displayed in the <div/> element for messages.

  3. The processResponse() function still needs to display a list of suggestions for searches without a match. Add the bold lines in the following code:

    function processResponse(data) {
        var result = JSON.parse(data);
        var messageDiv = document.getElementById("divSearchMessage");
    var resultsDiv = document.getElementById("divSearchResults");
        var message = "";
        var results = [];
        var length = result.results.length;
        var carStr = getCarString(result.searchedCar);
    
        if (result.isFound) {
            message = "We found " + length + " matches for " + carStr;
        } else {
            message = "We could not find " + carStr + ". You might like: ";
    
            for (var i = 0; i < length; i++) {
                results.push(getCarString(result.results[i]));
            }
    
           resultsDiv.innerHTML = results.join("<br/>");
        }
    
        messageDiv.innerHTML = message;
    }

    This new code adds two new variables: resultsDiv and results. The resultsDiv variable points to the <div/> element with an id of divSearchResults, and the results variable is an array.

    The next new code is a for loop that loops through the result.results array. Each element in this array is passed to the getCarString() function, and the resulting string value is pushed into the results array.

    When the loop exists, the results.join() method is called, concatenating the elements of the results array and separating each element with a <br/> element. The resulting string is assigned to the resultsDiv.innerHTML property to display the list of cars on the page.

  4. Save the file again and point your browser to http://localhost/Lesson35/lesson35_example01.htm. Fill out the form, submit it, and watch the random results.

To get the sample code files, download Lesson 35 from the book's website at www.wrox.com.

Note

Please select Lesson 35 on the DVD to view the video that accompanies this lesson.

Step-by-Step
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.178.53