Consuming JSON

The final task that we haven't covered yet is parsing JSON on the server side with PHP. There are two parts to this task: generate the JSON request with JavaScript and take that request and return results in JSON format. Just like with the XML format, we need to redesign our results slightly to accommodate multiple queries.

Designing the JSON Request and Response

The JSON request needs to include an array of search terms. The terms will consist of an object that has both a title and an author attribute. Here is an example of the JSON request:

[{"title":"PHP", "author":"curioso"}]

Our response will be the same as the responses from before only we now have to include the query in the response as well as the ability to have multiple responses. To refresh our memory, our old response to the query looked like this:

[{
       "Title":"Ajax with PHP 5",
       "Author":"Andrew Curioso",
       "Year":2007,
       "Publisher":"O'Reilly Media"
}]

When we add our query encapsulation information it looks like this:

[{
       "query":{ title:"PHP", author:"Curioso" },
       "Books":[{
              "Title":"Ajax with PHP 5",
              "Author":"Andrew Curioso",
              "Year":2007,
              "Publisher":"O'Reilly Media"
       }]
}]

The new response includes an array of objects that each contains two members. One of the members is a query object just like the one we send to the server and the other is an array of all the books returned in the result.

Generating the JSON Query with JavaScript

We use almost the same PHP file as with the previous example on consuming XML here. One thing that needs to change is the JavaScript inclusion. Instead of including searchXML2.js we include searchJSON2.js. The new file generates the JSON query as well as parses the results.

Here, we need to use json.js. Sure, we can write our own generator like we did with XML, but there is a perfectly good and free (i.e., in public domain) solution for us to use. (Download it here: http://www.json.org.)

The method that we use this time is toJSONString(). Using prototypes this method is added to several JavaScript classes automatically when the file is included. From the documentation found at the beginning of json.js (April 2007), we can see that these classes are as follows:

  • Array

  • Boolean

  • Date

  • Number

  • Object

  • String

These six classes can now be easily converted to their JSON representation using the aforementioned function. For example, let's say we have the variable foo, which is of type Object. We can call foo.toJSONString(), which provides us with a JSON representation of foo. This is assuming, of course, that we previously included the json.js file.

To use this method, we must first generate an object to parse. We can generate the object in real-time by accessing the members as if they already existed. Let's examine the following example:

var bookQuery = new Object();
bookQuery.title = "PHP";
bookQuery.author = "Curioso";
var JSONText = bookQuery.toJSONString());

There are two ways to produce the JSON data: create an object and then assign the members or generate the JSON code directly using strings. This may seem more appropriate in some cases:

var title = "PHP";
var author = "Curioso";
var JSONText = "{"title":""+title+"","author":""+author+""}";

One problem with this second method is that it can be extremely difficult to read and debug for large objects. The other problem, and this one is more important, is security. The toJSONString() method takes care to ensure that strings are escaped properly. Not doing this could result in arbitrary JavaScript being injected into the data in much the same way that SQL injection is done. With proper care the string can be manual escaped, however toJSONString() takes care of the messy part for us.

The JavaScript File

Now go ahead and edit the PHP file. This time, include searchJSON2.js instead of searchJSON1.js. Include json.js as well (from the http://www.json.org site). Since the former script (searchJSON2.js) doesn't exist yet, we'll create it. Fortunately, we can reuse much of the code from our searchJSON1.js and searchXML2.js files. We copy and paste the function from the appropriate files (see Table 2).

Table 2. Functions from files

searchJSON1.js

searchXML2.js

NewHTTP()

FetchValue()

AddToList()

SubmitQuery()

With those four functions out of the way, there are three left to write. BuildQuery() will build our JSON query in much the same way we did with the XML equivalent. OnData() will parse the JSON data. SubmitAjaxQuery() needs one small change.

Copy and paste SubmitAjaxQuery() from searchXML2.js just as we did before. We only need to make two changes. First, we change the content type to be "application/x-www-form-urlencoded" instead of "text/xml." Now we need to change the way that send() is called.

Instead of sending just the method BuildQuery() we need to change the send line to say:

httpObj.send("JSONQuery="+escape(BuildQuery()));

This time around we escape the query. When posting multipart form data, the information should be URL encoded. We are done with that function. Now that we have that function taken care of we can tackle the BuildQuery() function. To accomplish this, use the toJSONString() function discussed earlier.

function BuildQuery()
{
       var table = document.getElementById("formTable");
       var rows= table.getElementsByTagName("tr");

       var queries = new Array(rows.length-2);

       for ( var i=1; i<rows.length-1; i++ )
       {
              queries[i-1] = new Object();
              queries[i-1].title = document.getElementById(rows[i].id+"title").value;
              queries[i-1].author = document.getElementById(rows[i].id+"author")
.value;
       }
       return queries.toJSONString();
}

Finally, write the OnData() event handler.

function OnData()
{
       if ( httpObj.readyState==4 ) {
              var errorText = null;
              var table = document.getElementById("data");
              table.deleteRow(1);

              if (httpObj.status==200 ) {
                      try {
                           var queries = eval(httpObj.responseText);
                           var curRow = 1;
                           if ( queries == null )
                                  errorText = "Sorry, no queries were found.";
                           else for ( var i=0; i<queries.length; i++ ) {
                                  var row = table.insertRow(curRow);
                                  curRow++;
                                  row.innerHTML =
                                         "<td colspan=4>Search for: "+
                                         FetchValue(queries[i].query,"title") + " " +
                                         FetchValue(queries[i].query,"author")+
"</td>";

                                  for ( var j=0; j<queries[i].books.length; j++ ) {
                                         row = table.insertRow(curRow);
                                         curRow++;
                                         row.insertCell(0).innerHTML =
                                         FetchValue(queries[i].books[j],"Title");

                                         row.insertCell(1).innerHTML =
                                         FetchValue(queries[i].books[j],"Author");

                                         row.insertCell(2).innerHTML =
                                         FetchValue(queries[i].books[j],"Year");

                                         row.insertCell(3).innerHTML =
                                         FetchValue(queries[i].books[j],"Publisher");
                                  }
                           }
                     } catch (e) {
                           errorText = "Invalid results given."+e.message;
                     }
              } else {
                     alert("Error: Could not get search results.");
                     errorText = "Sorry, the search service is unavailable.";
              }
              httpObj = null;
              document.getElementById("Submit").disabled = false;
              document.getElementById("Add").disabled = false;
              if ( errorText != null ) {
                     var row = table.insertRow(1);
                     var cell = row.insertCell(0);
                     cell.colSpan = 4;
                     cell.innerHTML = errorText;
              }
       }
}

The only difference between this OnData() and the OnData() in the searchJSON1.js script is that we must now traverse the additional "query" tag to print out the section headers.

The PHP File

Rather than restate the entire file, this section will cover only the part that changed. Open up the file we created in the "Consuming XML" section. Now scroll down until you find the line that says:

if ( strpos($HTTP_RAW_POST_DATA,"<request>") !== false ) {

Replace the code from (and including) this line down to the else statement. Instead of parsing it as XML, we interpret the JSON data. Just to refresh your memory, we use json_encode() to generate the response. Appropriately, there is also a json_decode() function that undoes the encoding. Use both functions in this script.

if ( isset( $_POST["JSONQuery"] ) ) {
       try {
              $data = json_decode(stripslashes($_POST["JSONQuery"]));
              if ( !is_array($data) ) { throw new Exception("JSON Error"); }

              $response = array();
              foreach ( $data as $v ) {
                     if ( !isset($v->title) || !isset($v->author) )
                           throw new Exception("Invalid Query");
                     $response[] = array(
                                  "query" => array(
                                             "title" => $v->title,
                                             "author" => $v->author ),
                                  "books" =>
                                  BookLoader::Search($v->title, $v->author)
                                  );
              }
              echo json_encode($response);
       } catch ( Exception $e ) {
              header( "HTML/1.0 500 Internal server error" );
              echo "Error: ".$e->getMessage();
              exit;
       }
}

This code is similar to the XML version. We generate an array that, when serialized, provides the appropriate JSON output. The code to initialize $HTTP_RAW_POST_DATA can now be deleted since we no longer use it.

Review

In this section, we modified our XML consumer to consume and produce JSON instead. We also took full advantage of both json_encode() and json_decode() as well as the json.js class. Even though consuming JSON on the server is not yet common, it can be a lightweight way to transmit complex data.

..................Content has been hidden....................

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