To run this sample project in NetBeans,
download the
JSTL-XML.zip archive.
This uses the JSTL library files
(jstl.jar, standard.jar)
and these new JAR files:
Using Eclipse, download and install the WAR file:
JSTL-XML.war
XML
XML (eXtensible Markup Language)
is a common way of expressing hierarchical information
for data interchange between various applications.
Like HTML, it is a derivative of the exceedingly complex
SGML (Standard General Markup Language).
Web applications, as well as other many
other applications, need to know how to read,
validate and create such XML
files. This example we will several aspects of
XML usage
XML display using JSTL-based and XSLT-based scripts
XML transformation using XSL
XML transformation using JSTL x tags
Validation of XML files using DTD file
Validation of XML files using XSD file
Both the JSTL core (c) and XML (x) tags are used.
The XML file
Our example is a bookstore containing, of course, books
(at least they have different fields than usual).
Each book has a title, one or more authors, a year and an optional price.
The book belongs to a category and the title may express a language for
how it appears.
Here is the file of interest:
books.xml
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="COOKING">
<title>Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<year>2005</year>
<price>39.95</price>
</book>
<book category="FICTION">
<title lang="sp">La Casa De Los Espiritus</title>
<author>Isabelle Allende</author>
<year>1982</year>
<price>15.00</price>
</book>
</bookstore>
Basic Display
Our first two example merely capture the books.xml and
display it. At the core of both is the JSTL tag usage:
<c:import var="xml" url="static/books.xml" /<
which "captures" the entire XML file into the EL variable.
The scripts are these:
<%@page contentType="text/xml" pageEncoding="UTF-8"
%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
%><c:import var="xml" url="books.xml"
/><c:out value="${xml}" escapeXml="false" />
Notes about these scripts are:
xmlview1 shows what the literal XML code is by
dumping it within a pre-formatted (pre) element.
The crucial escaping is implicit using the c:out tag.
xmlview2 sends the XML to the browser, as
if you clicked on the unadorned
XML file, per se. In JSP you cannot have
any space characters
whatsoever up to the c:out display
in which we do not want to escape the XML.
The rendering which takes place is courtesy of the browser
capabilities; both Firefox and IE will render the XML
in some default manner. However, Eclipse's built-in browser
completely ignores the unknown tags.
Although this type of rendering would have better control
in a servlet, the c:import tag is hard to
beat for convenience.
Rendering via an XSL stylesheet
XSLT (eXtensible Stylesheet Language Transformations) is an XML-based language
which can be used to access information in an XML file via XPath expressions
and output it in a suitable format. This "suitable" output format is
often HTML, in which the content of the XML file can be viewed readily.
Simply by attaching the XSL file to the XML file books.xml
via the statement:
causes modern, XML-aware, browsers to do the transformation.
Here is the XSL file:
books.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="/bookstore">
<html>
<table cellpadding="5px" border="1">
<tr align="left">
<th>category</th>
<th>title</th>
<th>lang</th>
<th>authors</th>
<th>year</th>
<th>price</th>
</tr>
<xsl:for-each select="./book">
<tr valign="top">
<td><xsl:value-of select="./@category" /></td>
<td><xsl:value-of select="./title" /></td>
<td><xsl:value-of select="./title/@lang" /></td>
<td>
<xsl:for-each select="./author">
<xsl:value-of select="." />
<br />
</xsl:for-each>
</td>
<td><xsl:value-of select="./year" /></td>
<td>
<xsl:choose>
<xsl:when test="./price/text()">
<xsl:value-of select="./price" />
</xsl:when>
<xsl:otherwise>?</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:for-each>
</table>
</html>
</xsl:template>
</xsl:stylesheet>
The first approach is to simply pass this XML content
directly to the browser as we did before:
xmlview3.jsp
<%@page contentType="text/xml" pageEncoding="UTF-8"
%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
%><c:import var="xml" url="books-use-xsl.xml"
/><c:out value="${xml}" escapeXml="false" />
The only difference is that the books.xml has been modified
by adding an extra line, making it this file:
The type of notation used to access data in the XML
tree is called XPath notation.
Examples of the
XPath expressions used are in this class are:
"/bookstore/book" access the book elements
"./title/text() access the text of book selected
"./@category" access the category attribute of book selected
"./title/@lang" access the lang attribute of the title of book selected
We'll see the same expressions in JSTL.
Applying the XSL stylesheet in JSTL
The
x:transform tag makes it, in many ways,
easier to apply the stylesheet. In this case, the
XSL stylesheet's extra html tags, which were
necessary in the previous case, are superfluous here,
and are effectively ignored.
Additional XML (x) tags can be used to effect a conversion
from XML to HTML as is done by the XSL stylesheet.
Again, the XPath notation is used to extract the
XML information and embed in into HTML code using
the XML tags. Aside from
minor syntactic differences, the two conversions to HTML
is virtually identical.
Validation of an XML file signfies that that its structure, in terms of
elements and attributes matches an established pattern or schema.
DTD validation
The original validation
specification built into XML is called Document Type Definition (DTD). It can
be embedded within the XML file directly or, preferably, be accessed
as an external file as follows:
books-use-dtd.xml
<?xml version="1.0"?>
<!DOCTYPE bookstore SYSTEM "books.dtd">
<bookstore>
The external file DTD file is this:
books.dtd
<!ELEMENT bookstore (book)* >
<!ELEMENT book (title, author+, year, price?) >
<!ELEMENT title (#PCDATA) >
<!ELEMENT author (#PCDATA) >
<!ELEMENT year (#PCDATA) >
<!ELEMENT price (#PCDATA) >
<!ATTLIST title lang (en|sp|gr) #IMPLIED>
<!ATTLIST book category CDATA #REQUIRED>
XML Schema validation
The DTD schema has some significant limitations in terms of its
expressive capabilities.
For example, there is no real
notion of data types for elements or attributes. The XML schema is one such
alternative validation schema. It itself is written in XML and offers
significantly more expressive capabilities.
The reference to the
external file books.xsd is added
to the starting tag:
books-use-xsd.xml
<?xml version="1.0"?>
<bookstore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="books.xsd">
The external XSD validation file is significantly
more complicated than the DTD file:
books.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="bookstore">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="book" type="bookType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="bookType">
<xsd:sequence>
<!--
XML schema makes it tricky to have a non-empty element with
attributes. It has to be a complexType to have attributes
but you have to specify simpleContent
-->
<xsd:element name="title" minOccurs="1">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="lang" type="langType" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="author" type="xsd:string" maxOccurs="4"/>
<xsd:element name="year" type="yearType"/>
<xsd:element name="price" type="xsd:decimal" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="category" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:simpleType name="langType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="en"/>
<xsd:enumeration value="sp"/>
<xsd:enumeration value="gr"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="yearType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{4}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
Validation Example
This is the JSP file which sets up for validating either of
the two files:
books-use-dtd.xml
books-use-xsd.xml
There are several interesting new features in this
code per se, include the use of the EL global variable:
pageContext
which allows us to access features of JSP including the
request, response and session. In this example we make
use of the pageContext.request in order to construct
the full URL from variable information.
validate.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="validator" scope="request" class="util.DomValidator" />
<c:set var="title" value="XML Validation" />
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${title}</title>
</head>
<body>
<h1>${title}</h1>
<form action="">
XML file:
<select name="file">
<option>books-use-dtd.xml</option>
<option "${param.file=="books-use-xsd.xml"?"selected":""}"
>books-use-xsd.xml</option>
</select>
<input type="submit" value="Validate" />
</form>
<c:if test="${!empty(param.file)}" >
<br /><br />
<c:set var="xmlFile" value="${param.file}" />
<b><c:out value="${xmlFile}" /></b>
<!-- We have to construct the full URL to the sample file -->
<c:set var="server" value="${pageContext.request.serverName}" />
<c:set var="port" value="${pageContext.request.serverPort}" />
<c:set var="path" value="${pageContext.request.contextPath}" />
<c:set var="url" value="http://${server}:${port}${path}/${xmlFile}" />
<c:set target="${validator}" property="url" value="${url}" />
<c:set var="status" value="${validator.status}" />
<br /><br />
<c:choose>
<c:when test="${status == 0}">VALID</c:when>
<c:when test="${status > 0}">
WARNINGS:<br />
<c:out value="${validator.info}" />
</c:when>
<c:otherwise>
ERRORS:<br />
<c:out value="${validator.info}" />
</c:otherwise>
</c:choose>
</c:if>
</body>
</html>
The key code which performs the validation is below. The idea behind validation
is the ability to both
parse the XML file, i.e., make sure it is well-formed
match the content to standards specified by DTD, XSD or other
From a Java standpoint, parsing an XML file can be viewed according
to two types of parsing models:
A DOM (Document Object Model) or JDOM (Java DOM) Parser
A SAX (Simple API for XML) Parser
A DOM parser sees the XML
file as a tree structure consisting of a
heirarchical set of nodes; everything about the XML document is represented
in the tree structure.
The SAX parse process the XML file as a series of "events" in
which one enters or leaves an element tag, no representation of the entire
structure is ever created.
From a Java perspective, we use JAXP
(Java API for XML Parsing) which provides
programming for DOM, SAX and XSLT processing.
The DOM parser, although more costly in terms of space usage, provides the best
mechanism for XML validation.
The actual validation is done by the Java object:
DOMParser parser = new DOMParser();
in one simple step:
parser.parse(url);
where the url is the constructed URL to one of the files
books-use-dtd.xml or books-use-xsd.xml.
Prior to running the parser, several preparatory steps must be taken,
including:
Set the "features" describing which type(s) of validations the
parser will perform (DTD & XSD)
Associate an error handler object
parser.setErrorHandler(handler);
After execution, information set by the handler provides the means for determining
the degree of validity of the XML file. This is the validator bean code:
util/DomValidator.java
package util;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.DOMParser;
public class DomValidator {
private String url;
private String info;
private boolean error;
public String getInfo() { return info; }
public void setUrl(String url) { this.url = url; }
public int getStatus() {
try {
DOMParser parser = new DOMParser();
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setFeature(
"http://apache.org/xml/features/validation/schema", true);
error = false;
info = "";
ErrorHandler handler = new DefaultHandler() {
@Override
public void error(SAXParseException x) {
info += " *** Parse error: " + x.getMessage();
error = true;
}
@Override
public void warning(SAXParseException x) {
info += " *** Parse warning: " + x.getMessage();
}
@Override
public void fatalError(SAXParseException x) throws SAXParseException {
throw x;
}
};
parser.setErrorHandler(handler);
parser.parse(url);
if (error) {
return -1;
} else if (!info.equals("")) {
return 1;
}
return 0;
} catch (Exception x) {
info = " *** " + x.toString();
return -1;
}
}
}
Checking the validation
Here are some tweaks you can do to
the XML files
books-use-dtd.xml
books-use-xsd.xml
to get feedback. Open these files in your IDE, make these changes in both files,
test the validation and then change them back (undo):
Remove the year element in one of the books.
Remove the category attribute from one of the book elements.
Add the unknown attribute/value a="test" into one of the book elements.
Modify the XML statement by changing "en" to "en1"
<title lang="en1">XQuery Kick Start</title>
Change one of the years to an invalid string, like 200x.
The last example illustrates the limitations of the DTD file, which will say the false
year is valid, wherease the XSD file will catch the mistake. Another example is that
the XSD file indicates that there can be no more than four author elements, something
not expressible in DTD.