Chapter 8. XML and Data Binding

CONTENTS
  •  Data Binding in Internet Explorer
  •  Using Data Source Objects
  •  XML and Hierarchical Data
  •  Searching XML Data

In the previous chapter, we took a look at working with XML documents in Internet Explorer using the DOM. In that chapter, we used methods such as firstChild, lastChild, lastSibling, and so on to work through a document. Using methods such as those give you complete access to the data in an XML document, but regarding an XML document as a node tree can be confusing, especially if you forget that the character data in an element is stored in its own node.

There's another way of handling XML documents in Internet Explorer, however, and it also bears exploration, which we'll do in this chapter. Internet Explorer enables you to read both HTML and XML documents and to store them in a database. Using the database methods that we'll see in this chapter, you can move from record to record through your data in a way that many programmers find easier to use than the DOM methods.

Data Binding in Internet Explorer

Internet Explorer specializes in data binding. With data binding, you can connect the data in documents to an ActiveX Data Object (ADO) database and then work with that data in an easy way. This technique is useful because data from a database can be sent as an XML document over the Internet and can be immediately converted back to a database in the browser, which means that programmers familiar with database programming can concentrate on using database methods, not DOM methods.

I'll take a look at data binding in Internet Explorer in general first, and then I'll work specifically with XML documents in this chapter. There are two parts to working with bound data in Internet Explorer using data source objects (DSOs) and binding data to the HTML elements in a Web page.

More Details on Data Binding

You can find information about data binding in Internet Explorer at http://msdn.microsoft.com/workshop/c-frame.htm#/workshop/author/default.asp.

Using Data Source Objects

There are four data source objects in Internet Explorer: the Microsoft HTML (MSHTML) control, the tabular data control (TDC), the XML DSO, and XML data islands. (In fact, Internet Explorer also supports the relatively sophisticated Remote Data Service [RDS] DSO, which you use to connect to database applications, such as those that run in SQL-enabled application on a Web server.) Two of these DSOs, the XML DSO and XML data islands, support XML documents.

A DSO doesn't appear in a Web page (although, as we'll see at the end of the chapter, the XML DSO can display status messages in a page). You use a DSO to read a document and make its data available to the rest of the page. For a DSO to read data from a document, that data must be formatted correctly.

Here's an example HTML document, customer.htm, that holds data on sales made to customers. Here, I'm recording the customers' names and IDs, the date of the purchase, the department of the item purchased, and the name of the item. Note how I'm structuring an HTML page to hold data for Internet Explorer by using the <SPAN> element. (You can also use other elements such as <DIV>) and can assign a type to each data item with the enclosing element's ID tag. As you can see, this technique cries out for XML formatting.)

<HTML>     <HEAD>         <TITLE>             Customer Data         </TITLE>     </HEAD>     <BODY>         Name: <SPAN ID="NAME">Charles</SPAN><BR>         ID: <SPAN ID="CUSTOMER_ID">58704</SPAN><BR>         Purchase date: Date: <SPAN ID="PURCHASE_DATE">             10/15/2001</SPAN><BR>         Department: <SPAN ID="DEPARTMENT">             Meat</SPAN><BR>         Product: <SPAN ID="PRODUCT_NAME">Ham</SPAN><BR>         Name: <SPAN ID="NAME">Franklin</SPAN><BR>         ID: <SPAN ID="CUSTOMER_ID">58705</SPAN><BR>         Purchase date: <SPAN ID="PURCHASE_DATE">             10/15/2001</SPAN><BR>         Department: <SPAN ID="DEPARTMENT">             Produce</SPAN><BR>         Product: <SPAN ID="PRODUCT_NAME">Tomatoes</SPAN><BR>         Name: <SPAN ID="NAME">Phoebe</SPAN><BR>         ID: <SPAN ID="CUSTOMER_ID">58706</SPAN><BR>         Purchase date: <SPAN ID="PURCHASE_DATE">             10/15/2001</SPAN><BR>         Department: <SPAN ID="DEPARTMENT">             Meat</SPAN><BR>         Product: <SPAN ID="PRODUCT_NAME">Turkey</SPAN><BR>         Name: <SPAN ID="NAME">Mark</SPAN><BR>         ID: <SPAN ID="CUSTOMER_ID">58707</SPAN><BR>         Purchase date: <SPAN ID="PURCHASE_DATE">             10/15/2001</SPAN><BR>         Department: <SPAN ID="DEPARTMENT">             Meat</SPAN><BR>         Product: <SPAN ID="PRODUCT_NAME">Beef</SPAN><BR>         Name: <SPAN ID="NAME">Nancy</SPAN><BR>         ID: <SPAN ID="CUSTOMER_ID">58708</SPAN><BR>         Purchase date: <SPAN ID="PURCHASE_DATE">             10/15/2001</SPAN><BR>         Department: <SPAN ID="DEPARTMENT">             Frozen</SPAN><BR>         Product: <SPAN ID="PRODUCT_NAME">Broccoli</SPAN><BR>         </BODY>     </HTML>

The most common way of handling data formatted as HTML documents is with the MSHTML DSO, and I'm going to use it here. A DSO reads a document like this one and converts it into a recordset. Each record in the recordset comes from the HTML or XML elements that you've used to store the data. For example, here's what the HTML for one record looks like:

Name: <SPAN ID="NAME">Charles</SPAN><BR> ID: <SPAN ID="CUSTOMER_ID">58704</SPAN><BR> Purchase date: Date: <SPAN ID="PURCHASE_DATE">     10/15/2001</SPAN><BR> Department: <SPAN ID="DEPARTMENT">     Meat</SPAN><BR> Product: <SPAN ID="PRODUCT_NAME">Ham</SPAN><BR>

This record has five fields: NAME, CUSTOMER_ID, PURCHASE_DATE, DEPARTMENT, and PRODUCT_NAME. A recordset is much like an array holding records; when you work with a particular record, you can access the data in the fields in each record individually. For example, to determine what item Charles has purchased, you just need to find his record and then check the PRODUCT_NAME field in that record.

Using the Internet Explorer <OBJECT> element, you can create an MSHTML DSO and bind it to customer.htm. Here, I'm naming this DSO dsoCustomer:

<OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0"> </OBJECT>

The DSO will read and interpret customer.htm and convert that document into an ADO recordset (the type of recordset actually used in Internet Explorer is read-only, called an ADOR recordset). The DSO holds data from only one record at a time, and that record is called the current record. You can use the built-in methods of a recordset to navigate through your data by making other records the current record; some common methods are moveFirst, moveLast, moveNext, and movePrevious, which let you navigate from record to record. To actually display the data from this DSO, you can bind it to HTML elements.

Binding Data to HTML Elements

Quite a few elements in Internet Explorer support data properties that you can use to bind them to DSO. To connect to those properties, you use the DATASRC and DATAFLD attributes in those elements. You set the DATASRC attribute to the name of a DSO, and you set the DATAFLD attribute to the name of the data field to which you want to bind the element. The element will then display the data in the current record in the DSO. You can use the moveFirst, moveLast, moveNext, and movePrevious methods to make other records the current record, and the data in the bound elements will be updated automatically.

For example, if you've bound a text field control to the dsoCustomer DSO and to the NAME field in the DSO's records, that control will display the name "Charles" when the page first loads. Executing the moveNext method will make the next record in the recordset the current record, and the text field will display the name Franklin.

Here's a list of HTML elements in Internet Explorer detailing what property is actually bound when you use the DATASRC and DATAFLD attributes:

Element Bound Properties
A Binds to the href property; does not update data.
APPLET Binds to the param property; updates data.
BUTTON Binds to the value property; does not update data.
DIV Binds to the innerText and innerHTML properties; does not update data.
FRAME Binds to the src property; does not update data.
IFRAME Binds to the src property; does not update data.
IMG Binds to the src property; does not update data.
INPUT TYPE=BUTTON Binds to the value property; does not update data.
INPUT TYPE=CHECKBOX Binds to the checked property; updates data.
INPUT TYPE=HIDDEN Binds to the value property; updates data.
INPUT TYPE=PASSWORD Binds to the value property; updates data.
INPUT TYPE=RADIO Binds to the checked property; updates data.
INPUT TYPE=TEXT Binds to the value property; updates data.
LABEL Binds to the value property; does not update data.
MARQUEE Binds to the innerText and innerHTML properties; does not update data.
OBJECT Binds to the objects property; updates data.
PARAM Binds to the param property; updates data.
SELECT Binds to the text property of an option; updates data.
SPAN Binds to the innerText and innerHTML properties; does not update data.
TABLE Constructs an entire table; does not update data.
TEXTAREA Binds to the value property; updates data.

In addition, HTML tags have certain events that you use with data bindings:

Event Description
onafterupdate Happens after the data in the element is updated to the DSO
onbeforeunload Happens before the page is unloaded
onbeforeupdate Happens just before the data in the element is updated in the DSO
onerrorupdate Happens if there an error stopped data from being updated in the DSO

It's time to put this to work. I'll start by adding an MSHTML control named dsoCustomer to a Web page, and connecting that DSO to customer.htm:

<HTML>     <HEAD>         <TITLE>             Data Binding With the MSHTML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Data Binding With the MSHTML DSO             </H1>             <OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0">             </OBJECT>             .             .             .

Now I'll bind this DSO to a text field by setting that text field's DATASRC attribute to #dsoCustomer (Internet Explorer requires the # symbol before a DSO's name). Because a text field can display only one field of data at a time, I'll bind the NAME field to this control by setting its DATAFLD attribute to NAME:

<HTML>     <HEAD>         <TITLE>             Data Binding With the MSHTML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Data Binding With the MSHTML DSO             </H1>             <OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0">             </OBJECT>             Name: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE="10">             .             .             .

I'll bind the CUSTOMER_ID field to another text field as well. I can also display text data from the DSO directly in a Web page without using a text field control by binding that DSO to a <SPAN> element in this way:

<HTML>     <HEAD>         <TITLE>             Data Binding With the MSHTML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Data Binding With the MSHTML DSO             </H1>             <OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0">             </OBJECT>             Name: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE="10">             <P>             ID: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="CUSTOMER_ID" SIZE="5">             <P>             Purchase date: <SPAN DATASRC="#dsoCustomer"                 DATAFLD="PURCHASE_DATE"></SPAN>     .     .     .

To show how to bind to other controls, I'll bind the DEPARTMENT field, which can take the values Produce, Meat, or Frozen, to a <SELECT> control, which displays a drop-down list. You bind this control to the dsoCustomer DSO as you do other controls, but you must also specify all possible values that the field you're binding, DEPARTMENT, can take as <OPTION> elements in the <SELECT> control, like this:

<HTML>     <HEAD>         <TITLE>             Data Binding With the MSHTML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Data Binding With the MSHTML DSO             </H1>             <OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0">             </OBJECT>             Name: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE="10">             <P>             ID: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="CUSTOMER_ID" SIZE="5">             <P>             Purchase date: <SPAN DATASRC="#dsoCustomer"                 DATAFLD="PURCHASE_DATE"></SPAN>             <P>             Department: <SELECT DATASRC="#dsoCustomer"                 DATAFLD="DEPARTMENT" SIZE="1">                 <OPTION VALUE="Produce">Produce                 <OPTION VALUE="Meat">Meat                 <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             Product: <SPAN DATASRC="#dsoCustomer" DATAFLD="PRODUCT_NAME">             </SPAN>     .     .     .

Note that I'm also binding the PRODUCT_NAME field to another <SPAN> element. When the page first loads, you'll see the customer name, customer ID, purchase date, department, and product ID of the first record displayed in the elements that we've put in the page. But there's a problem as you may recall, DSOs don't appear in the page, so how can the user move from record to record?

To let the user navigate through the recordset, you use the recordset's moveFirst, moveLast, moveNext, and movePrevious methods, and connect those methods to buttons. You can reach the recordset object inside the DSO as dsoCustomer.recordset, so using the moveFirst method to move to the first record in the recordset looks like this: dsoCustomer.recordset.moveFirst(). Following common usage, I'll give the buttons these captions:

Caption Action
<< Moves to the first record
< Moves to the previous record
> Moves to the next record
>> Moves to the last record

Here's what the HTML for these buttons looks like:

<BUTTON ONCLICK=     "dsoCustomer.recordset.moveFirst()" >&lt;&lt; </BUTTON> <BUTTON ONCLICK     "dsoCustomer.recordset.movePrevious()" >&lt; </BUTTON> <BUTTON ONCLICK     "dsoCustomer.recordset.moveNext()" >&gt; </BUTTON> <BUTTON ONCLICK=     "dsoCustomer.recordset.moveLast()">&gt;&gt; </BUTTON>

Before using the moveNext and movePrevious methods, however, it's worth checking to make sure that there actually is a next or previous record to move to. (If you move past the end of the recordset, the bound elements in your page will appear blank.) You can use the recordset object's BOF (beginning of file) property to see if you're at the beginning of the recordset, and the EOF (end of file) property to see if you're at the end of the recordset. To make sure that we're not trying to move outside the recordset, I'll use this code:

<HTML>     <HEAD>         <TITLE>             Data Binding With the MSHTML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Data Binding With the MSHTML DSO             </H1>             <OBJECT ID="dsoCustomer" DATA="customer.htm" HEIGHT="0" WIDTH="0">             </OBJECT>             Name: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE="10">             <P>             ID: <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="CUSTOMER_ID" SIZE="5">             <P>             Purchase date: <SPAN DATASRC="#dsoCustomer"                 DATAFLD="PURCHASE_DATE"></SPAN>             <P>             Department: <SELECT DATASRC="#dsoCustomer"                 DATAFLD="DEPARTMENT" SIZE="1">                 <OPTION VALUE="Produce">Produce                 <OPTION VALUE="Meat">Meat                 <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             Product: <SPAN DATASRC="#dsoCustomer" DATAFLD="PRODUCT_NAME">             </SPAN>             <P>             <BUTTON ONCLICK=                 "dsoCustomer.recordset.moveFirst()" >&lt;&lt;             </BUTTON>             <BUTTON ONCLICK="if (!dsoCustomer.recordset.BOF)                 dsoCustomer.recordset.movePrevious()" >&lt;             </BUTTON>             <BUTTON ONCLICK="if (!dsoCustomer.recordset.EOF)                 dsoCustomer.recordset.moveNext()" >&gt;             </BUTTON>             <BUTTON ONCLICK=                 "dsoCustomer.recordset.moveLast()">&gt;&gt;             </BUTTON>         </CENTER>     </BODY> </HTML>

You can see this page in operation in Figure 8.1. As you see in that page, the data from customer.htm is displayed. The user can move from record to record using the buttons at the bottom of the page.

Figure 8.1. Using data binding in Internet >Explorer.

graphics/08fig01.gif

So much for data binding and HTML; it's time to start working with XML.

Data Binding with XML

I'll start by converting customer.htm into XML. In customer.htm, I had to use the ID attribute of <SPAN> elements to name the fields in a record; in XML, I can simply create a new element. Here's what customer.htm looks like in XML format:

<?xml version="1.0"?> <CUSTOMERS>     <CUSTOMER>         <NAME>Charles</NAME>         <CUSTOMER_ID>58704</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>         <PRODUCT_NAME>Ham</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Franklin</NAME>         <CUSTOMER_ID>58705</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Produce</DEPARTMENT>         <PRODUCT_NAME>Tomatoes</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Phoebe</NAME>         <CUSTOMER_ID>58706</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>         <PRODUCT_NAME>Turkey</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Mark</NAME>         <CUSTOMER_ID>58707</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>     <PRODUCT_NAME>Beef</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Nancy</NAME>         <CUSTOMER_ID>58708</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Frozen</DEPARTMENT>         <PRODUCT_NAME>Broccoli</PRODUCT_NAME>     </CUSTOMER> </CUSTOMERS>

As you can see, each record has become a <CUSTOMER> element. You can use whatever name you want for elements, and Internet Explorer will understand what you mean. In the previous HTML example, I used the MSHTML control as a DSO, but that's not going to work here. Instead, you can use either an XML data island or a special applet-based XML DSO that comes with Internet Explorer. I'm going to start with XML data islands.

XML Single-Record Binding Using XML Data Islands

To see how to bind HTML elements to an XML data island, I'll write an example. In this case, I'll add a data island for customer.xml with the ID customers to a new Web page, like this:

<HTML>     <HEAD>         <TITLE>             Single Record Binding Using XML Data Islands         </TITLE>     </HEAD>     <XML SRC="customer.xml" ID="customers"></XML>     .     .     .

Now customers can act like a DSO, just like any other DSO (which is why data islands are called data islands). I can bind this DSO to assorted HTML elements as we've already seen in this chapter:

<HTML>     <HEAD>         <TITLE>             Single Record Binding Using XML Data Islands         </TITLE>     </HEAD>     <XML SRC="customer.xml" ID="customers"></XML>     <BODY>         <CENTER>             <H1>                 Single Record Binding Using XML Data Islands             </H1>             Name: <INPUT TYPE="TEXT" DATASRC="#customers"                 DATAFLD="NAME" SIZE=10>             <P>             CUSTOMER_ID: <INPUT TYPE="TEXT" DATASRC="#customers"                 DATAFLD="CUSTOMER_ID" SIZE=5>             <P>             Department: <SELECT DATASRC="#customers"                 DATAFLD="DEPARTMENT" SIZE=1>                 <OPTION VALUE="Meat">Meat                 <OPTION VALUE="Produce">Produce                 <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             Purchase date: <SPAN DATASRC="#customers"                 DATAFLD="PURCHASE_DATE"></SPAN>             <P>             Product: <SPAN DATASRC="#customers" DATAFLD="PRODUCT_NAME"></SPAN><P>             .             .             .         </CENTER>     </BODY> </HTML>
Properties, Methods, and Events of XML DSOs

In the example at the beginning of this chapter, we saw that you could use recordset methods such as moveFirst, moveLast, moveNext, and movePrevious to move around in a recordset. I'll make that more systematic now. In particular, the recordset object in an XML DSO has these properties:

Property Description
absolutePage The page where the current record is
absolutePosition The position in a recordset of the current record
BOF True if the current record position is before the first record
cacheSize The number of records from a recordset object that are cached locally
cursorLocation The location of the cursor for the recordset
cursorType The type of database cursor used
editMode Specification for whether editing is in progress
EOF True if the current position is after the last record
lockType The type of database locking in force
maxRecords The maximum number of records to return to a recordset from a query
pageCount The number of pages of data that the recordset contains
pageSize The number of records that make up one page
recordCount The number of records in the recordset
state The state of the recordset (open or closed)
status The status of the current record
stayInSync Specification for whether a hierarchical recordset should remain in contact with the data source

Here are the methods of the recordset objects inside XML DSOs:

Method Description
addNew Adds a new record to the recordset.
cancel Cancels execution of a pending Execute or Open request.
cancelUpdate Cancels a pending update operation.
clone Creates a copy of the recordset.
close Closes a recordset.
delete Deletes the current record (or group of records).
find Searches the record set (although the Structured Query Language syntax required here is not supported in Internet Explorer yet).
getRows Reads records and stores them in an array.
getString Gets the recordset as a string.
move Moves the position of the current record.
moveFirst, moveLast, moveNext, movePrevious Enable you to navigate to various positions in the recordset.
nextRecordSet Clears the current recordset object and returns the next recordset. This is used with hierarchical recordsets.
open Opens a database.
requery Re-executes the query that created the recordset.
save Saves the recordset in a file.
supports Indicates the features that the recordset supports. You must pass long integer values that correspond to the various ADO methods, as defined in the Microsoft ADO documentation. For example, passing this method a value of 0x1000400 (0x specifies a hexadecimal value) returns a value of true, indicating that the recordset supports the addNew method; passing a value of 0x10000 returns a value of false, indicating that the recordset does not support the updateBatch method.

XML DSOs also have a number of events that you can handle. (Recall that we saw how to handle XML document events in Internet Explorer at the end of the previous chapter.)

Event Description
onDataAvailable Happens each time a batch of data is downloaded
onDatasetChanged Happens when the data set was changed
onDatasetComplete Happens when the data is downloaded and ready for use
onReadyStateChange Happens when the ReadyState property changes
onRowEnter Happens when a new record becomes the current one
onRowExit Happens just before exiting the current record
onRowsDelete Happens when a row is deleted
onRowsInserted Happens when a row is inserted
onCellChange Happens when the data in a bound control changes and the focus leaves that cell

To let the user navigate around in the recordset created from customer.xml, I'll add the same buttons that we saw in the earlier HTML example, using methods such as customers.recordset.moveNext() to navigate, like this:

<HTML>     <HEAD>         <TITLE>Single Record Binding Using XML Data Islands</TITLE>     </HEAD>     <XML SRC="customer.xml" ID="customers"></XML>     <BODY>         <CENTER>             <H1>                 Single Record Binding Using XML Data Islands             </H1>             Name: <INPUT TYPE="TEXT" DATASRC="#customers"                 DATAFLD="NAME" SIZE=10>             <P>             Customer ID: <INPUT TYPE="TEXT" DATASRC="#customers"                 DATAFLD="CUSTOMER_ID" SIZE=5>             <P>             Purchase date: <SPAN DATASRC="#customers"                 DATAFLD="PURCHASE_DATE"></SPAN><P>             Product: <SPAN DATASRC="#customers" DATAFLD="PRODUCT_NAME"></SPAN>             <P>             Department: <SELECT DATASRC="#customers"                 DATAFLD="DEPARTMENT" SIZE=1>             <OPTION VALUE="Meat">Meat             <OPTION VALUE="Produce">Produce             <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             <BUTTON ONCLICK="customers.recordset.moveFirst()" >                  &lt;&lt;             </BUTTON>             <BUTTON ONCLICK="if (!customers.recordset.BOF)                 customers.recordset.movePrevious()" >                 &lt;             </BUTTON>             <BUTTON ONCLICK="if (!customers.recordset.EOF)                 customers.recordset.moveNext()" >                 &gt;             </BUTTON>             <BUTTON ONCLICK="customers.recordset.moveLast()">                 &gt;&gt;             </BUTTON> </CENTER>     </BODY> </HTML>

You can see this page in Figure 8.2, where you see the fields of the current record displayed in bound HTML elements. The user can click the buttons in the page to navigate through the recordset.

Figure 8.2. Using data binding to display an XML document in Internet Explorer.

graphics/08fig02.gif

As you can see in the previous table, there's a lot more you can do with a recordset than just navigate through it. For example, it's often useful to access the individual fields in a record. Say that you wanted to get the value in the CUSTOMER_ID field of the current record in the DSO. You could use the expression customers.recordset("CUSTOMER_ID") to do that.

You might note how much easier accessing data in recordset fields is than using DOM methods such as nextChild and lastSibling to navigate a node tree. If you consider an XML document as a database, it can make life a good deal easier.

I'll access individual fields in records now in an example to make this concrete. In this case, I'll loop over all the records in the database (which I can do with a while loop, looping until the recordset's EOF property is true) and display the customer name, the item purchased, and what department the purchase was made in. Here's what the code looks like:

<HTML>     <HEAD>         <TITLE>             Accessing individual data fields         </TITLE>         <XML ID="customer" SRC="customer.xml"></XML>         <SCRIPT LANGUAGE="JavaScript">             function viewData()             {                 while (!customer.recordset.EOF) {                     div1.innerHTML +=                     customer.recordset("NAME") +                     " bought " +                     customer.recordset("PRODUCT_NAME") +                     " from the " +                     customer.recordset("DEPARTMENT") +                     " department.<BR>"                     customer.recordset.moveNext()                 }             }         </SCRIPT>     </HEAD>     <BODY>         <CENTER>             <H1>                 Accessing individual data fields             </H1>         </CENTER>         <FORM>             <CENTER>                 <INPUT TYPE="BUTTON" VALUE="View data"                     ONCLICK="viewData()">             </CENTER>         </FORM>         <DIV ID="div1">         </DIV>     </BODY> </HTML>

You can see this page in operation in Figure 8.3. As you can see there, the data from the individual fields in the various records has been assembled and displayed.

Besides working with single records as we have up to this point, you can also work with all the data in an XML document at once when you bind it to a table.

Figure 8.3. Accessing individual data fields.

graphics/08fig03.gif

Tabular Data Binding and XML

When you bind a recordset to an HTML table, the table can display the entire recordset. Here's an example; in this case, I'll bind the data in customer.xml to a table. I start by creating an XML data island, giving the data island the ID customers:

<HTML>     <HEAD>         <TITLE>             Tabular Binding with XML Data Islands         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Tabular Binding with XML Data Islands             </H1>             <XML SRC="customer.xml" ID="customers"></XML>             .             .             .

To bind the data in customer.xml to a table, all I have to do is set a table's DATASRC attribute to customers:

<HTML>     <HEAD>         <TITLE>             Tabular Binding with XML Data Islands         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Tabular Binding with XML Data Islands             </H1>             <XML SRC="customer.xml" ID="customers"></XML>              <TABLE DATASRC="#customers" CELLSPACING="10">              .              .              .

The fields in the records of customer.xml are NAME, CUSTOMER_ID, PURCHASE_DATE, DEPARTMENT, and DATE. I will bind those fields to the individual cells in a table. Here's how that works:

<HTML>     <HEAD>         <TITLE>             Tabular Binding with XML Data Islands         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Tabular Binding with XML Data Islands             </H1>             <XML SRC="customer.xml" ID="customers"></XML>              <TABLE DATASRC="#customers" CELLSPACING="10">                  <THEAD>                      <TR>                          <TH>Name</TH>                          <TH>Customer ID</TH>                          <TH>Purchase Date</TH>                          <TH>Department</TH>                          <TH>Product</TH>                      </TR>                   </THEAD>                   <TBODY>                       <TR>                           <TD>                               <SPAN DATAFLD="NAME">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="CUSTOMER_ID">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="PURCHASE_DATE">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="DEPARTMENT">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="PRODUCT_NAME">                               </SPAN>                           </TD>                        </TR>                   </TBODY>             </TABLE>         </CENTER>     </BODY> </HTML>

You can see the results in Figure 8.4, where the data from customer.xml is displayed in a table.

Figure 8.4. Binding data to a table in Internet Explorer.

graphics/08fig04.gif

There's another DSO that you can use with XML documents in Internet Explorer the XML DSO.

Single-Record Data Binding with the XML DSO

Starting in Internet Explorer 4, Microsoft has included an XML DSO expressly designed to be used with XML. This DSO is a little odd because it's not internal to Internet Explorer; instead, it's implemented as a Java applet. You can embed this applet in a page and create an XML DSO like this with the HTML <APPLET> element:

<APPLET     CODE="com.ms.xml.dso.XMLDSO.class"     ID="IDNAME"     WIDTH="0"     HEIGHT="0"     MAYSCRIPT="true">     <PARAM NAME="URL" VALUE="XMLPageURL"> </APPLET>

Here, you pass the URL of the XML document as a parameter to the XML DSO applet, using the <PARAM> element, and then give this DSO a name with the <APPLET> ID attribute.

In the next example, I'll put the XML DSO to work, connecting it to customer.xml. To bind customer.xml to HTML elements, I start by adding the XML applet to a Web page, calling this DSO dsoCustomer, and then passing it the URL of the document to read as a parameter:

<HTML>     <HEAD>         <TITLE>             Single Record Binding Using the XML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Single Record Binding Using the XML DSO             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="dsoCustomer"                 WIDTH="0" HEIGHT="0"                 MAYSCRIPT="true">                 <PARAM NAME="URL" VALUE="customer.xml">             </APPLET>             .             .             .

That's all it takes. This DSO exposes a recordset object as XML data islands do, so I can bind HTML elements to it as we've done before:

<HTML>     <HEAD>         <TITLE>             Single Record Binding Using the XML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Single Record Binding Using the XML DSO             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="dsoCustomer"                 WIDTH="0" HEIGHT="0"                 MAYSCRIPT="true">                <PARAM NAME="URL" VALUE="customer.xml">             </APPLET>             Name:             <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE=10>             <P>             Customer ID:             <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="CUSTOMER_ID" SIZE=5>             <P>             Purchase date:             <SPAN DATASRC="#dsoCustomer"                 DATAFLD="PURCHASE_DATE"></SPAN>             <P>             Department:             <SELECT DATASRC="#dsoCustomer"                 DATAFLD="DEPARTMENT" SIZE=1>                 <OPTION VALUE="Meat">Meat                 <OPTION VALUE="Produce">Produce                 <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             Product:             <SPAN DATASRC="#dsoCustomer" DATAFLD="PRODUCT_NAME">             </SPAN>             .             .             .

I can use the recordset object's methods, such as moveNext to move to the next record, or movePrevious to move to the previous one, with buttons like this:

<HTML>     <HEAD>         <TITLE>             Single Record Binding Using the XML DSO         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Single Record Binding Using the XML DSO             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="dsoCustomer"                 WIDTH="0" HEIGHT="0"                 MAYSCRIPT="true">                 <PARAM NAME="URL" VALUE="customer.xml">             </APPLET>             Name:             <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="NAME" SIZE=10>             <P>             Customer ID:             <INPUT TYPE="TEXT" DATASRC="#dsoCustomer"                 DATAFLD="CUSTOMER_ID" SIZE=5>             <P>             Purchase date:             <SPAN DATASRC="#dsoCustomer"                 DATAFLD="PURCHASE_DATE"></SPAN>             <P>             Department:             <SELECT DATASRC="#dsoCustomer"                 DATAFLD="DEPARTMENT" SIZE=1>                 <OPTION VALUE="Meat">Meat                 <OPTION VALUE="Produce">Produce                 <OPTION VALUE="Frozen">Frozen             </SELECT>             <P>             Product:             <SPAN DATASRC="#dsoCustomer" DATAFLD="PRODUCT_NAME">             </SPAN>             <P>             <BUTTON ONCLICK="dsoCustomer.recordset.moveFirst()" >                  &lt;&lt;             </BUTTON>             <BUTTON ONCLICK="if (!dsoCustomer.recordset.BOF)                 dsoCustomer.recordset.movePrevious()" >                 &lt;             </BUTTON>             <BUTTON ONCLICK="if (!dsoCustomer.recordset.EOF)                 dsoCustomer.recordset.moveNext()" >                 &gt;             </BUTTON>             <BUTTON ONCLICK="dsoCustomer.recordset.moveLast()">                 &gt;&gt;             </BUTTON>         </CENTER>     </BODY> </HTML>

You can see this page at work in Figure 8.5. The XML DOS applet works as expected, but the fact that it has remained an applet external to Internet Explorer suggests that Microsoft may discard it sooner or later in favor of XML islands.

Figure 8.5. Single-record binding using the XML DSO in Internet Explorer.

graphics/08fig05.gif

As with XML data islands, you can also bind the XML DSO to tables, as described in the next section.

Tabular Data Binding with the XML DSO

It's as easy to bind the XML DSO to tables as it was to bind XML data islands to tables. Here's an example to show how this works. In this case, I'm just binding customer.xml to a table using the XML DSO, and I'm displaying all the fields in the various records of customer.xml at once:

<HTML>     <HEAD>         <TITLE>             Binding the XML DSO to Tables         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Binding the XML DSO to Tables             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="customers"                 WIDTH="0" HEIGHT="0"                 MAYSCRIPT="true">                 <PARAM NAME="URL" VALUE="customer.xml">             </APPLET>              <TABLE DATASRC="#customers" CELLSPACING="10">                  <THEAD>                      <TR>                          <TH>Name</TH>                          <TH>Customer ID</TH>                          <TH>Purchase Date</TH>                          <TH>Department</TH>                          <TH>Product</TH>                      </TR>                   </THEAD>                   <TBODY>                       <TR>                           <TD>                               <SPAN DATAFLD="NAME">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="CUSTOMER_ID">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="PURCHASE_DATE">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="DEPARTMENT">                               </SPAN>                           </TD>                           <TD>                               <SPAN DATAFLD="PRODUCT_NAME">                               </SPAN>                           </TD>                        </TR>                   </TBODY>             </TABLE>         </CENTER>     </BODY> </HTML>

That's all it takes; you can see this page in action in Figure 8.6.

Figure 8.6. Tabular data binding with the XML DSO in Internet Explorer.

graphics/08fig06.gif

XML and Hierarchical Data

One of the most interesting developments in database handling is the capability to create hierarchical recordsets, where a record can actually contain an entire new recordset. XML documents represent a perfect way of storing hierarchical recordsets because you can enclose one set of elements inside another easily.

Here's an example; in this case, I'm adding records about deliveries made to the customers in customer.xml in a new XML document, deliveries.xml:

<?xml version="1.0"?> <CUSTOMERS>     <CUSTOMER>         <NAME>Charles</NAME>         <RECORD>             <CUSTOMER_ID>58704</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Ham</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$1.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$1.49</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Franklin</NAME>         <RECORD>             <CUSTOMER_ID>58705</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Produce</DEPARTMENT>             <PRODUCT_NAME>Tomatoes</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$3.00</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$2.95</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Phoebe</NAME>         <RECORD>             <CUSTOMER_ID>58706</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Turkey</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$4.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$8.99</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Mark</NAME>         <RECORD>             <CUSTOMER_ID>58707</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Beef</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$3.95</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$6.95</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Nancy</NAME>         <RECORD>             <CUSTOMER_ID>58708</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Frozen</DEPARTMENT>             <PRODUCT_NAME>Broccoli</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$1.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$2.99</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER> </CUSTOMERS>

In this case, each <RECORD> element itself contains two <DELIVERY> elements (which contain <DATE> and <TOTAL_COST> elements). A DSO can't simply treat multiple enclosed records like this as a single record because that would give two or more fields in the record the same name. Instead, Internet Explorer makes the recordset into a hierarchical recordset and gives each <DELIVERY> element its own subrecordset.

How do you refer to a subrecordset in a hierarchical database? For example, how can you refer to the <DELIVERY> elements in each <RECORD> element? You do that by referring to a new recordset, RECORD.DELIVERY. This expression refers to the child recordset made up of the <DELIVERY> elements in the current record.

As usual, this is made easier to understand with an example, so take a look at this code. Here, I'm binding deliveries.xml to a table and displaying the <DELIVERY> records for each customer using tables. I start by binding a table to an XML data island and displaying the name of each customer, like this:

<HTML>     <HEAD>         <TITLE>             Using XML With Hierarchical Records         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Using XML With Hierarchical Records             </H1>             <XML SRC="deliveries.xml" ID=dsoCustomer></XML>             <TABLE DATASRC="#dsoCustomer" BORDER="1">                 <TR>                     <TH><DIV DATAFLD="NAME"></DIV></TH>                     <TD>             .             .             .

Next, I bind a table to the RECORD field in the current record:

<HTML>     <HEAD>         <TITLE>             Using XML With Hierarchical Records         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Using XML With Hierarchical Records             </H1>             <XML SRC="deliveries.xml" ID=dsoCustomer></XML>             <TABLE DATASRC="#dsoCustomer" BORDER="1">                 <TR>                     <TH><DIV DATAFLD="NAME"></DIV></TH>                     <TD>                         <TABLE DATASRC="#dsoCustomer"                         DATAFLD="RECORD">             .             .             .

To display the data from the <DATE> and <TOTAL_COST> elements in each <DELIVERY> record, I bind one final internal table to the RECORD.DELIVERY recordset:

<HTML>     <HEAD>         <TITLE>             Using XML With Hierarchical Records         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Using XML With Hierarchical Records             </H1>             <XML SRC="deliveries.xml" ID=dsoCustomer></XML>             <TABLE DATASRC="#dsoCustomer" BORDER="1">                 <TR>                     <TH><DIV DATAFLD="NAME"></DIV></TH>                     <TD>                         <TABLE DATASRC="#dsoCustomer"                         DATAFLD="RECORD">                             <TR>                                 <TD>                                 <TABLE DATASRC="#dsoCustomer"                                     CELLPADDING = "5"                                     DATAFLD="RECORD.DELIVERY">                                     <TR ALIGN = "LEFT">                                         <TH>Date</TH>                                         <TH>Total Cost</TH>                                     </TR>                                     <TR ALIGN = "LEFT">                                         <TD><DIV DATAFLD="DATE">                                         </DIV></TD>                                         <TD><DIV                                         DATAFLD="TOTAL_COST">                                         </DIV></TD>                                     </TR>                                 </TABLE>                                 </TD>                             </TR>                         </TABLE>                     </TD>                 </TR>             </TABLE>         </CENTER>     </BODY> </HTML>

This page appears in Internet Explorer in Figure 8.7. As you can see there, each customer's name is displayed next to the dates and costs of their deliveries. Now you're handling hierarchical recordsets and XML.

Figure 8.7. Displaying hierarchical recordsets in Internet Explorer.

graphics/08fig07.gif

Handling Variable-Size Hierarchical Data in XML Documents

We've seen that Internet Explorer DSOs can handle hierarchical recordsets when each record itself has an internal recordset. The internal recordsets that I used each contained two records, but that's hardly a realistic example; in real-world documents, recordsets can be of any length. How do the DSOs in Internet Explorer stack up here? Take a look at this new document, variable.xml, in which each internal recordset has between one and three <DELIVERY> records:

<?xml version="1.0"?> <CUSTOMERS>     <CUSTOMER>         <NAME>Charles</NAME>         <RECORD>             <CUSTOMER_ID>58704</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Ham</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$1.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$1.49</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$1.49</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Franklin</NAME>         <RECORD>             <CUSTOMER_ID>58705</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Produce</DEPARTMENT>             <PRODUCT_NAME>Tomatoes</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$3.00</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Phoebe</NAME>         <RECORD>             <CUSTOMER_ID>58706</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Turkey</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$4.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$8.99</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Mark</NAME>         <RECORD>             <CUSTOMER_ID>58707</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Meat</DEPARTMENT>             <PRODUCT_NAME>Beef</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$3.95</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$6.95</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER>     <CUSTOMER>         <NAME>Nancy</NAME>         <RECORD>             <CUSTOMER_ID>58708</CUSTOMER_ID>             <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>             <DEPARTMENT>Frozen</DEPARTMENT>             <PRODUCT_NAME>Broccoli</PRODUCT_NAME>             <DELIVERY>                 <DATE>10/20/2001</DATE>                 <TOTAL_COST>$1.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>10/25/2001</DATE>                 <TOTAL_COST>$2.99</TOTAL_COST>             </DELIVERY>             <DELIVERY>                 <DATE>5-3-2002</DATE>                 <TOTAL_COST>$7200.00</TOTAL_COST>             </DELIVERY>         </RECORD>     </CUSTOMER> </CUSTOMERS>

In fact, this is not a problem; here's the page I'll use to display this data:

<HTML>     <HEAD>         <TITLE>             Variable Size Hierarchical Records         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Variable Size Hierarchical Records             </H1>             <XML SRC="variable.xml" ID="customers"></XML>             <TABLE DATASRC="#customers" BORDER="1">                 <TR>                     <TH><DIV DATAFLD="NAME"></DIV></TH>                     <TD>                         <TABLE DATASRC="#customers"                         DATAFLD="RECORD">                             <TR>                                 <TD>                                 <TABLE DATASRC="#customers"                                     CELLPADDING = "3"                                     DATAFLD="RECORD.DELIVERY">                                     <TR ALIGN = "LEFT">                                         <TH>Date</TH>                                         <TH>Amount</TH>                                     </TR>                                     <TR ALIGN = "LEFT">                                         <TD><DIV DATAFLD="DATE">                                         </DIV></TD>                                         <TD><DIV                                         DATAFLD="TOTAL_COST">                                         </DIV></TD>                                     </TR>                                 </TABLE>                                 </TD>                             </TR>                         </TABLE>                     </TD>                 </TR>             </TABLE>         </CENTER>     </BODY> </HTML>

You can see the results in Figure 8.8, where each <DELIVERY> recordset is correctly displayed, even though they have different number of records.

Figure 8.8. Variable-size hierarchical records in Internet Explorer.

graphics/08fig08.gif

You can also create the same page using the XML DSO applet instead of data islands:

<HTML>     <HEAD>         <TITLE>             Variable Size Hierarchical Records         </TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>                 Variable Size Hierarchical Records             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="customers"                 WIDTH="0" HEIGHT="0"                 MAYSCRIPT="true">                 <PARAM NAME="URL" VALUE="variable.xml">             </APPLET>             <TABLE DATASRC="#customers" BORDER="1">                 <TR>                     <TH><DIV DATAFLD="NAME"></DIV></TH>                     <TD>                         <TABLE DATASRC="#customers"                         DATAFLD="RECORD">                             <TR ALIGN = CENTER>                                 <TD>Sales</TD>                             </TR>                             <TR>                             .                             .                             .                             </TR>                         </TABLE>                     </TD>                 </TR>             </TABLE>         </CENTER>     </BODY> </HTML>

Searching XML Data

You can do a great many things with recordsets, as we've seen in this chapter. In this chapter's final example, I'll take a look at how to search a database for a specific item. In particular, I'll let the user search for a match to a customer's name that this user specifies.

For this example, I'll modify customer.xml by adding a second customer with the name Nancy to make sure that we catch all instances of a match, naming this new document multiple.xml:

<?xml version="1.0"?> <CUSTOMER>     <CUSTOMER>         <NAME>Charles</NAME>         <CUSTOMER_ID>58704</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>         <PRODUCT_NAME>Ham</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Franklin</NAME>         <CUSTOMER_ID>58705</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Produce</DEPARTMENT>         <PRODUCT_NAME>Tomatoes</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Phoebe</NAME>         <CUSTOMER_ID>58706</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>         <PRODUCT_NAME>Turkey</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Mark</NAME>         <CUSTOMER_ID>58707</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Meat</DEPARTMENT>     <PRODUCT_NAME>Beef</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Nancy</NAME>         <CUSTOMER_ID>58708</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Frozen</DEPARTMENT>         <PRODUCT_NAME>Broccoli</PRODUCT_NAME>     </CUSTOMER>     <CUSTOMER>         <NAME>Nancy</NAME>         <CUSTOMER_ID>58709</CUSTOMER_ID>         <PURCHASE_DATE>10/15/2001</PURCHASE_DATE>         <DEPARTMENT>Produce</DEPARTMENT>         <PRODUCT_NAME>Tomatoes</PRODUCT_NAME>     </CUSTOMER> </CUSTOMER>

After setting up an XML data island and connecting it to multiple.xml, I'll define a new function named findMatches that will search for matches to the customer name the user wants to search for. Although ADOR recordset objects do have a method called find for searching databases, you set the search criterion in that method using a Structured Query Language (SQL, the language many database applications use) expression, and Internet Explorer doesn't appear to support such expressions. Instead, I'll set up a JavaScript loop that will find matches to the name the user is searching for.

The user can enter the name to search for in a text field that I'll call text1. When that user clicks a button, the findMatches function is called. I'll convert the name the user wants to search for into lowercase (using the JavaScript String object's toLowerCase method) to make the search case-insensitive, and I'll store it in a variable named searchFor:

<HTML>     <HEAD>         <TITLE>             Searching XML-Based Databases         </TITLE>         <XML ID="customers" SRC="multiple.xml"></XML>         <SCRIPT LANGUAGE="JavaScript">             function findMatches()             {                 var searchFor = form1.text1.value.toLowerCase()     .     .     .

Now I'll loop over all the records in the recordset, storing the name in the current record in a variable named currentName, which I also convert to lowercase:

<HTML>     <HEAD>         <TITLE>             Searching XML-Based Databases         </TITLE>         <XML ID="customers" SRC="multiple.xml"></XML>         <SCRIPT LANGUAGE="JavaScript">             function findMatches()             {                 var searchFor = form1.text1.value.toLowerCase()                 while (!customers.recordset.EOF) {                     var currentName = new String(customers.recordset("NAME"))                     currentName = currentName.toLowerCase()                     .                     .                     .                     customers.recordset.moveNext()                 }             }         </SCRIPT>     </HEAD>

I'll use the JavaScript String object's indexOf method to see whether the current name matches the name that the user is searching for. The indexOf method returns a value of 0 or greater if a match was found, so I use that method like this:

<HTML>     <HEAD>         <TITLE>             Searching XML-Based Databases         </TITLE>         <XML ID="customers" SRC="multiple.xml"></XML>         <SCRIPT LANGUAGE="JavaScript">             function findMatches()             {                 var searchFor = form1.text1.value.toLowerCase()                 while (!customers.recordset.EOF) {                     var currentName = new String(customers.recordset("NAME"))                     currentName = currentName.toLowerCase()                     if (currentName.indexOf(searchFor) >= 0) {                     .                     .                     .                     }                     customers.recordset.moveNext()                 }             }         </SCRIPT>     </HEAD>     .     .     .

All that remains is to display the matching records in a <DIV> element, as we have before in this chapter, and add the button and text field that we'll use to let the user interact with our code:

<HTML>     <HEAD>         <TITLE>             Searching XML-Based Databases         </TITLE>         <XML ID="customers" SRC="multiple.xml"></XML>         <SCRIPT LANGUAGE="JavaScript">             function findMatches()             {                 var searchFor = form1.text1.value.toLowerCase()                 while (!customers.recordset.EOF) {                     var currentName = new String(customers.recordset("NAME"))                     currentName = currentName.toLowerCase()                     if (currentName.indexOf(searchFor) >= 0) {                         divMessage.innerHTML +=                         customers.recordset("NAME") +                         " (ID " +                         customers.recordset("CUSTOMER_ID") +                         ") bought " +                         customers.recordset("PRODUCT_NAME") +                         " from the " +                         customers.recordset("DEPARTMENT") +                         " department on " +                         customers.recordset("PURCHASE_DATE") +                         ".<BR>"                     }                     customers.recordset.moveNext()                 }             }         </SCRIPT>     </HEAD>     <BODY>         <CENTER>             <H1>                 Searching XML-Based Databases             </H1>             <FORM ID="form1">                 Search for this name: <INPUT TYPE="TEXT" NAME="text1">                 <BR>                 <BR>                 <INPUT TYPE="BUTTON" VALUE="Search for matches"                     ONCLICK="findMatches()">             </FORM>         </CENTER>         <DIV ID="divMessage">         </DIV>     </BODY> </HTML>

You can see the results in Figure 8.9, where we've matched both customers named Nancy.

Figure 8.9. Searching for matches in XML-based databases.

graphics/08fig09.gif

In this example, I've used XML data islands, but of course you can also use the XML DSO applet to load the file multiple.xml. I'll do that here to demonstrate another aspect of the XML DSO applet: Up until now, I've given the applet a zero width and height, but in fact, this applet does display the status of its operations if you give it some space in the Web page. I'll do that like this:

<HTML>     <HEAD>         <TITLE>             Searching XML-Based Databases         </TITLE>         <SCRIPT LANGUAGE="JavaScript">             function findMatches()             {                 var searchFor = form1.text1.value.toLowerCase()                 while (!customers.recordset.EOF) {                     var currentName = new String(customers.recordset("NAME"))                     currentName = currentName.toLowerCase()                     if (currentName.indexOf(searchFor) >= 0) {                         divMessage.innerHTML +=                         customers.recordset("NAME") +                         " (ID " +                         customers.recordset("CUSTOMER_ID") +                         ") bought " +                         customers.recordset("PRODUCT_NAME") +                         " from the " +                         customers.recordset("DEPARTMENT") +                         " department on " +                         customers.recordset("PURCHASE_DATE") +                         ".<BR>"                     }                     customers.recordset.moveNext()                 }             }         </SCRIPT>     </HEAD>     <BODY>         <CENTER>             <H1>                 Searching XML-Based Databases             </H1>             <APPLET CODE="com.ms.xml.dso.XMLDSO.class"                 ID="customers"                 WIDTH="400" HEIGHT="50"                 MAYSCRIPT="true">                 <PARAM NAME="URL" VALUE="multiple.xml">             </APPLET> <FORM ID="form1">                 Search for this name: <INPUT TYPE="TEXT" NAME="text1">                 <BR>                 <BR>                 <INPUT TYPE="BUTTON" VALUE="Search for matches"                     ONCLICK="findMatches()">             </FORM>         </CENTER>         <DIV ID="divMessage">         </DIV>     </BODY> </HTML>

You can see the results in Figure 8.10, where the XML DSO is displaying the message Successfully loaded XML from "file:/C:/xml/multiple.xml". The background of this applet is green because there was no error; if an error occurred, the background would be red and the applet would display error messages.

Figure 8.10. Searching for matches in XML-based databases using the XML DSO.

graphics/08fig10.gif

That completes our data-binding work with the Internet Explorer. In the next chapter, I'll take a look at XML and cascading style sheets.

CONTENTS


Inside XML
Real World XML (2nd Edition)
ISBN: 0735712867
EAN: 2147483647
Year: 2005
Pages: 23
Authors: Steve Holzner

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net