10.7 Filtering XML

   

You can filter XML documents with a SAX filter. [13] SAX, which stands for Simple API for XML, is a language-independent, event-based API for parsing XML. SAX reports parsing eventssuch as the start and end of elementsthrough callback methods . For example, consider the following simple XML document:

[13] You can read more about SAX at http://www.saxproject.org .

 <?xml version='1.0'?>  <document>    <greeting>Welcome</greeting> </document> 

SAX represents the preceding XML document as a series of events, like this:

  start document event   start element event:  document  start element event:  greeting  characters event:  Welcome  end element event:  greeting  end element event:  document  end document event  

SAX applications handle those events by registering an event handler and implementing methods that correspond to the events listed above.

As of SAX 2.0, you can filter XML documents by inserting a filter between the SAX parser and the application that handles SAX events. Using JSTL to filter XML documents with a SAX filter is a simple two-step process:

  1. Implement a SAX filter.

  2. Specify a reference to the filter implemented in step 1 as the value of the <x:parse> action's filter attribute.

Figure 10-6 shows a Web application that implements the steps listed above to filter specific elements in an XML document

Figure 10-6. Filtering XML

graphics/10fig06.jpg

The Web application shown in Figure 10-6 is similar to the Web application discussed in "Using Transformation Parameters" on page 446, except the former uses a SAX filter to filter elements in XML documents, whereas the latter removes columns from the HTML table with an XSLT stylesheet.

The top picture in Figure 10-6 shows the application's welcome page. The middle picture shows the XSLT-generated HTML table with email elements filtered, and the bottom picture shows the HTML table with home phone elements filtered.

Listing 10.11 lists the welcome page for the application shown in Figure 10-6.

Listing 10.11 index.jsp (Using SAX Filters)
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>    <head>       <title>Using SAX Filters</title>    </head>    <body>       <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %>       <%@ taglib uri='http://java.sun.com/jstl/xml'  prefix='x' %>       <%-- Import the XML document and XSLT stylesheet and store            the result in session-scoped variables, which are            accessed below and in apply_filter.jsp --%>       <c:import var='rolodex_xml' scope='session'                 url='rolodex.xml'/>       <c:import var='rolodex_xsl' scope='session'                 url='rolodex.xsl'/>       <%-- Transform the XML document with the XSLT stylesheet --%>       <x:transform xml='${rolodex_xml}' xslt='${rolodex_xsl}'/>       <%-- Import the form --%>       <c:import url='form.jsp'/>    </body> </html> 

The preceding JSP page imports the XML document and XSLT stylesheet and stores the resulting scoped variables in session scope. Subsequently, that JSP page performs an initial transformation and imports the JSP page that creates the form. That JSP page form.jsp is listed in Listing 10.12.

The preceding JSP page creates a form with an HTML select element that retains its value when the JSP page is reloaded. [14] That form's action is apply_filter.jsp , which is listed in Listing 10.13.

[14] See "Retaining Values for HTML Option Elements" on page 129 for more information about HTML select elements that retain their values.

The preceding JSP page uses <jsp:useBean> to create a SAX filter. A <c:set> action within the body of the <jsp:useBean> action specifies the type of element that the filter filters with the value of the filterThis request parameter. Subsequently, the JSP page parses the XML document, specifying the filter with the <x:parse> filter attribute. The JSP page then performs the transformation, specifying the name of the filtered element with a transformation parameter. Finally, the JSP page includes form.jsp so the user can subsequently select a different element to filter.

Listing 10.12 form.jsp (Creating a Selection Form)
 <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%-- Create a form that lets the user select an inventory      column to filter --%> <form action='apply_filter.jsp'>    Filter an element:    <select name='filterThis'>       <option value='NONE'          <c:if test='${param.filterThis == "NONE"}'>             selected          </c:if> >NONE       </option>       <option value='email'          <c:if test='${param.filterThis == "email"}'>             selected          </c:if> >email       </option>       <option value='phone@home'          <c:if test='${param.filterThis == "phone@home"}'>             selected          </c:if> >home phone       </option>       <option value='phone@work'          <c:if test='${param.filterThis == "phone@work"}'>             selected          </c:if> >work phone       </option>    </select>    <p><input type='submit' value='Filter XML'/> </form> 

SAX filters are straightforward to implement. Although it's not strictly necessary, most SAX filters extend the org.xml.sax.helpers.XMLFilterImpl class, which implements the SAX handler interfaces. That base class simply forwards all handler events to an XML reader; implementing a SAX filter involves extending that class and selectively overriding those handler methods as desired.

Listing 10.13 apply_filter.jsp
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>    <head>       <title>Using SAX Filters</title>    </head>    <body>       <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %>       <%@ taglib uri='http://java.sun.com/jstl/xml'  prefix='x' %>       <%-- Create a filter -- n ElementFilter instance -- and           set the name of the element that the filter filters --%>       <jsp:useBean id='filter' class='filters.ElementFilter'>  <c:set target='${filter}' property='elementToFilter'   value='${param.filterThis}'/>  </jsp:useBean>       <%-- Parse the XML document with the filter --%>       <x:parse var='document' xml='${rolodex_xml}'             filter='${filter}'/>       <%-- Transform the XML document with the XSLT stylesheet --%>       <x:transform xml='${document}' xslt='${rolodex_xsl}'>  <x:param name='filteredColumn'   value='${param.filterThis}'/>  </x:transform>       <%-- Import the form --%>       <c:import url='form.jsp'/>    </body> </html> 

The SAX filter created by the preceding JSP page is listed in Listing 10.14. That filter is a general-purpose filter that filters elements from an XML document. You can specify those elements in two ways: with the name of the element, which filters all elements with that name, or with an element/attribute combination specified with this syntax: element @ attribute. Elements, and optionally attributes, are specified with the filter's setElementToFilter method. The filter overrides the startElement , endElement , and characters methods, which are defined by the org.xml.sax.ContentHandler interface.

After the JSP page listed in Listing 10.13 on page 456 parses the Rolodex XML file with the preceding SAX filter, that JSP page performs a transformation that transforms the filtered XML document into an HTML table. The XSLT stylesheet that performs that transformation rolodex.xsl is listed in Listing 10.15.

Listing 10.14 WEB-INF/classes/filters/ElementFilter.java
 package filters;  import org.xml.sax.helpers.XMLFilterImpl;  import org.xml.sax.Attributes; import org.xml.sax.SAXException; // A filter that filters a specific element or an // element/attribute combination public class ElementFilter  extends XMLFilterImpl  {    private String filterThisElement,                   filterThisAttribute;    private boolean filtering; // A flag that tracks filtering state    public ElementFilter() {       // The element and attribute are unspecified when the filter       // is created, and the filtering flag is set to false       filterThisElement = filterThisAttribute = null;       filtering  = false;    }    public void setElementToFilter(String filterThis) {       int position;       // If there's an at-sign in the filterThis string...       if((position = filterThis.indexOf("@")) != -1) {          // Interpret the part of the string before the at-sign          // as the element name          filterThisElement = filterThis.substring(0, position);          // Interpret the part of the string after the at-sign          // as the attribute name          filterThisAttribute = filterThis.substring(position+1);       }       else { // There's not an at-sign in the filterThis string          filterThisElement = filterThis;       }    }    public void  characters  (char[] chars, int start, int length)                throws SAXException {       // If we're not filtering, pass on the event       if(!filtering)          super.characters(chars, start, length);    }    public void  startElement  (String uri, String localName,                             String qName, Attributes attrs)                             throws SAXException {       // If this element matches the element we need to filter...       if(localName.equals(filterThisElement)) {          // If an attribute was specified...          if(filterThisAttribute != null) {             // Loop over attributes looking for a match...             for(int i=0; i < attrs.getLength(); ++i) {                // If this attribute matches the attribute we're                // supposed to filter...                if(attrs.getValue(i).equals(filterThisAttribute)) {                   // Set filtering to true and break out of loop                   filtering = true;                   break;                }             }          }          else { // If no attribute was specified             filtering = true;          }       }       // If we're not filtering this element, pass on the       // event       if(!filtering)          super.startElement(uri, localName, qName, attrs);    }    public void  endElement  (String uri, String localName,                             String qName)                             throws SAXException {       // If we're not filtering, pass on the event       if(filtering == false)          super.endElement(uri, localName, qName);       // If we're filtering, stop       if(filtering)          filtering = false;    } } 

The preceding stylesheet is passed a transformation parameter that specifies the table column that corresponds to the filtered element. That parameter is used to omit the specified table column. Additionally, the stylesheet creates a table row for each contact element and table data for all of the contact element's child elements.

Listing 10.15 rolodex.xsl (Transforming a Filtered XML Document)
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                   version="1.0">    <!-- Declare the filteredColumn parameter-->  <xsl:param name='filteredColumn'/>  <!-- Generate HTML for the document root -->    <xsl:template match='/'>       <table border='1'>          <tr>             <th>First Name</th>             <th>Last Name</th>             <th>Company</th>             <xsl:if test='$filteredColumn != "email"'>                <th>Email</th>             </xsl:if>             <xsl:if test='$filteredColumn != "phone@work"'>                <th>Work Phone</th>             </xsl:if>             <xsl:if test='$filteredColumn != "phone@home"'>                <th>Home Phone</th>             </xsl:if>             <xsl:apply-templates/>          </tr>       </table>    </xsl:template>    <!-- Create a table row for each item and apply templates -->    <xsl:template match='contact'>       <tr><xsl:apply-templates/></tr>    </xsl:template>    <!-- Create table data for each item's children and         apply templates -->    <xsl:template match='contact/*'>       <td><xsl:apply-templates/></td>    </xsl:template> </xsl:stylesheet> 
   


Core JSTL[c] Mastering the JSP Standard Tag Library
Core JSTL[c] Mastering the JSP Standard Tag Library
ISBN: 131001531
EAN: N/A
Year: 2005
Pages: 124

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