Java Architecture for XML Binding (JAXB)
JAXB provides a means of automatically binding XML with Java objects.
JAXB is being developed through the Java Community Process (JCP) under JSR-31.
The home of JAXB is http://java.sun.com/xml/jaxb/index.html.
JAXB can be considered a serialization
mechanism from Java objects to XML. Serialization is the process of converting
an object in memory into a stream of data, and vice versa. Serialization is a
convenient way of storing objects on disk or sending them over a network.
Object serialization based on serializable and externalizable interfaces
performs a similar function but requires the developer to simply implement one
of these interfaces. In the case of JAXB, a set of binding classes is generated
using a schema compiler. The classes manage marshalling,
meaning translating Java objects to XML and back again. Here is a brief summary
of some of benefits of JAXB:
Valid data is guaranteed.
Marshalling is based on a schema, which constrains the structure of the XML.
JAXB is faster and requires less
memory when compared with DOM. DOM includes a lot of functionality for
manipulating arbitrary documents. JAXB applications are specific to a given
schema, so they can be more efficient.
JAXB is relatively easy to use.
All you need to do is supply a schema and generate binding classes using a
schema compiler. From there, reading, writing, and modify-ing XML is simply a
matter of a few method calls.
JAXB applications are extensible.
The generated classes can be used as is, or they can be subclassed for
reusability and added functionality.
Data Binding
If you think about it, a class and a schema perform similar functions.
Classes describe Java objects, whereas schemas describe XML documents. An
object is an instance of a class, and a document follows a schema. The diagram
in Figure 7.3 illustrates the rela-tionships between schemas, classes,
documents, and objects.
If we have a schema, perhaps in the form of a DTD, we can automatically
generate classes that translate between objects and documents. Of course, in
the real world, we might want to customize some of the details of the generated
classes. Fortunately, JAXB provides facilities for customization through an
optional binding schema.
One way to define a binding is to generate one Java class for every
element in a schema. If you don’t provide any extra information, this is
basically what JAXB does. Attributes within an element are mapped to String fields. Content within an
element is a little more complicated. Table 7.6 summarizes how the content is
mapped within a Java class.
TABLE 7.6 Default Content Binding
JAXB Example
Let’s look at an example using JAXB. A sample DTD is shown in Listing
7.16. We will use this DTD as our schema for generating binding classes. As of
this writing, the JAXB schema compiler only accepts DTDs. In the future, other
schema formats may be accepted.
LISTING 7.16 library.dtd
<?xml version=”1.0” encoding=”US-ASCII”?>
<!ELEMENT library (fiction|biography|science)*>
<!ELEMENT fiction (book)+>
<!ELEMENT biography (book)+>
<!ELEMENT science (book)+>
<!ELEMENT book (#PCDATA)>
<!ATTLIST book author
CDATA #REQUIRED>
This DTD describes a simple library with three categories of books:
fiction, biography, and science. Each of these categories can contain one or
more book elements. Each book element contains an author attribute, and the
title will be defined in the content of the book element. This isn’t
necessarily a practical example, but it will give you a good idea how JAXB
works. It can be easily expanded as needed.
In order to generate binding classes, we need to run the schema compiler
shipped with JAXB. The schema compiler is itself written in Java and can be
invoked like this:
java com.sun.tools.xjc.Main -d
outdir -roots library
library.dtd
Of course, the schema compiler JAR file must be in your classpath
(dropping the JAR into your JDK/jre/lib/ext directory is the simplest way). The -d option specifies an output directory for the generated classes. If it’s
not included, the current directory is used. The -roots option specifies a comma-separated list of root elements. This is
needed because DTDs don’t provide a way to define root elements. The last
argument is the filename of our DTD.
The schema compiler can also accept an optional binding schema. The
binding schema is an XML file with the extension .xjs. It can include information such as the root ele-ments, names of
classes and methods, which elements to bind to classes, types for attrib-utes,
and data conversions. If you specify a binding schema, you can avoid the -roots option. If you don’t use a
binding schema, you must supply the -roots option. There’s quite a bit you can do with binding schemas, so it’s
best to refer to the JAXB specifica-tion to get it all. We will stick with the
default bindings provided automatically with the schema compiler. In many cases,
this is good enough.
Once the schema compiler is run on our sample DTD, five Java source
files are gener-ated containing the classes that describe each of the elements
in library.dtd. The
code for the root element, Library.java, is shown in Listing 7.17.
LISTING 7.17 Library.java
// imports not
shown
public class Library
extends MarshallableRootElement implements
RootElement
{
private List _Content =
PredicatedLists.createInvalidating(this, new ContentPredicate(), new
ArrayList());
private PredicatedLists.Predicate pred_Content
= new ContentPredicate();
public List getContent() { return
_Content;
}
public void deleteContent() {
_Content = null; invalidate();
}
public
void emptyContent() {
_Content =
PredicatedLists.createInvalidating(this, pred_Content, new ArrayList());
}
LISTING 7.17 continued
public
void validateThis()
throws
LocalValidationException
{
}
public void validate(Validator v)
throws StructureValidationException
{
for (Iterator
i = _Content.iterator(); i.hasNext(); ) {
v.validate(((ValidatableObject)
i.next()));
}
}
public void marshal(Marshaller m)
throws IOException
{
XMLWriter w = m.writer(); w.start(“library”);
if
(_Content.size()> 0) {
for (Iterator
i = _Content.iterator(); i.hasNext(); ) { m.marshal(((MarshallableObject)
i.next()));
}
}
w.end(“library”);
}
public void
unmarshal(Unmarshaller u) throws UnmarshalException
{
XMLScanner xs = u.scanner(); Validator v =
u.validator(); xs.takeStart(“library”); while (xs.atAttribute()) {
String an = xs.takeAttributeName(); throw new
InvalidAttributeException(an);
}
{
List l =
PredicatedLists.create(this, pred_Content, new ArrayList());
while ((xs.atStart(“fiction”)||xs.atStart(“biography”))||
xs.atStart(“science”)) {
l.add(((MarshallableObject) u.unmarshal()));
}
_Content =
PredicatedLists.createInvalidating(this, pred_Content, l);
}
xs.takeEnd(“library”);
}
public static Library
unmarshal(InputStream in) throws UnmarshalException
{
return
unmarshal(XMLScanner.open(in));
}
public static Library
unmarshal(XMLScanner xs) throws UnmarshalException
{
return unmarshal(xs, newDispatcher());
}
public static Library
unmarshal(XMLScanner xs, Dispatcher d) throws UnmarshalException
{
return ((Library) d.unmarshal(xs, (Library.class)));
}
public boolean equals(Object ob)
{ if (this == ob) {
return true;
}
if (!(ob instanceof Library)) {
return false;
}
Library tob = ((Library) ob); if (_Content!= null) {
if (tob._Content == null) {
return false;
}
if (!_Content.equals(tob._Content))
{ return false;
}
} else {
if (tob._Content!= null) { return
false;
}
}
return true;
}
public int hashCode() { int h =
0;
h = ((127 *h)+((_Content!= null)?_Content.hashCode(): 0)); return h;
}
public
String toString() {
StringBuffer sb =
new
StringBuffer(“<<library”);
LISTING 7.17 continued
if (_Content!= null) {
sb.append(“ content=”);
sb.append(_Content.toString());
}
sb.append(“>>”); return sb.toString();
}
public static Dispatcher
newDispatcher() { return Biography.newDispatcher();
}
private static class
ContentPredicate implements PredicatedLists.Predicate
{
public void check(Object
ob) {
if (!(ob instanceof
MarshallableObject)) { throw new InvalidContentObjectException(ob,
(MarshallableObject.class));
}
}
}
}
There is a field named _Content of type java.util.List. This object can contain any number of elements—specifically, the
categories of books in our library. A List object is used for the content because we didn’t specify a particular
type in a binding schema. In this case, the schema compiler chose a List object because our library
element can con-tain a variable number of sub-elements. There are a few
validation methods that can vali-date this class and all content. The marshal() and unmarshal() methods read and write XML to
and from streams.
A simple application that exercises the generated classes is shown in
Listing 7.18. This application reads an XML file, adds another book element,
validates the XML, and writes all the content to a second XML file. This is
typical of the kinds of applications that can be developed with JAXB.
LISTING 7.18 LibraryApp.java
import java.io.*; import java.util.*; import javax.xml.bind.*;
import javax.xml.marshal.*;
public class LibraryApp {
protected Library myLibrary;
public LibraryApp() { myLibrary =
new Library();
}
public static void main(String[]
args) throws Exception { LibraryApp la = new LibraryApp();
la.readXML(“library.xml”);
la.addBook();
la.validate(); la.writeXML(“new_library.xml”);
}
public void readXML(String
fileName) throws Exception { System.out.println(“Reading “ + fileName);
FileInputStream fIn = new FileInputStream(fileName); try {
myLibrary =
myLibrary.unmarshal(fIn); } finally {
fIn.close();
}
System.out.println(myLibrary);
}
public
void addBook() {
List entryList =
myLibrary.getContent();
for (ListIterator
i = entryList.listIterator(); i.hasNext();) { Object element = i.next();
if (element instanceof Science) {
Book qmBook = new Book();
qmBook.setAuthor(“Eisberg, Resnick”);
qmBook.setContent(“Quantum Mechanics”); Science sb = (Science) element;
List sl = sb.getBook(); sl.add(qmBook);
break;
}
}
}
public void validate() throws
Exception { myLibrary.validate();
}
public void writeXML(String fileName) throws Exception {
System.out.println(“Writing “ + fileName);
LISTING 7.18 continued
FileOutputStream fOut = new FileOutputStream(fileName); try {
myLibrary.marshal(fOut); }
finally {
fOut.close();
}
}
}
The two imported packages of interest are javax.xml.bind and javax.xml.marshal. The first one, javax.xml.bind, contains most of the classes for JAXB. The second pack-age, javax.xml.marshal, contains a few classes needed
for marshalling. These classes were split into two packages because marshalling
is not specific to XML. There could be marshalling classes for all kinds of
data bindings.
The readXML() method reads an XML file into a Library object using the unmarshal() method. This could throw an UnmarshalException caused by invalid XML.
The addBook() method obtains a reference to the content of the Library object as a List object. It then searches for a
category of type science, creates a book object, and adds it to the science
category. Notice the setAuthor() method defined in the Book class. This was also generated by the schema compiler.
The validate() method validates the Library object before it is written using the writeXML()
method. Validation is required if any of the
objects describing our document were
modified. If validation is not done, an exception would be thrown by the marshal() method.
As you can see, JAXB is fairly easy to use. A lot of functionality can
be added automati-cally by specifying a binding schema. As mentioned earlier,
you might want to explore binding schemas in detail to get the most out of
JAXB.
Let’s take a
look at a case study so you can see how DOM works in the real world. We will
build a Java servlet that accepts a SQL statement to query a database and
returns the results in the form of XML. This might seem like overkill. Why not
just connect to the database through ODBC or JDBC and obtain a result set? Of
course, you could do that, but there are firewall con-straints and possibly the
need for a persistent connection to the client. HTTP is a simple protocol that
any firewall will pass without complaining. HTTP is easy to implement and easy
to debug. Most importantly, it’s an open protocol with wide industry support.
What we will
end up with is something like an XML data server. Of course,
com-mercial-quality data servers do a lot more, such as manage scalability
through caching and load balancing. Even still, we can build an effective data
server for illustration purposes, and scalability can be addressed later.
A servlet accepts a request
from a client and returns results in XML. The servlet acts as the “glue”
between the Internet and the database. Figure 7.4 illustrates the operation of
the servlet and database.
It would be
nice to automate as much of the XML generation as possible. What we can do is
use the column names of the database result set as the element names of our XML
output. We can use ResultSetMetaData from JDBC to give us this
information. The source code for our XML servlet is shown in Listing 7.19.
The complete
source code is available on the Sams Web site. Our sample data-base contains
information from the 2000 CIA World Fact
Book. In order to experiment with XMLServlet, you will need a servlet
engine such as Apache Tomcat. Tomcat is freely available for download from http://Jakarta.apache.org/tomcat. The download includes detailed installa-tion
instructions along with a number of examples. You can use almost any database
for testing. Any one of the sample databases supplied with Microsoft Access
will work well.
Sample output
is shown in Listing 7.20.
LISTING 7.19 XMLServlet.java
package
com.madhu.xml;
import java.io.*; import java.util.*; import java.sql.*;
import javax.servlet.*; import
javax.servlet.http.*;
import
org.jdom.*;
import
org.jdom.output.*;
public class XMLServlet extends
HttpServlet { protected Connection connection;
public void init() { try {
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);
connection = DriverManager.getConnection(
“jdbc:odbc:worldfactbook”); }
catch (Exception e) {
e.printStackTrace();
}
}
public
void
doGet(HttpServletRequest request,
HttpServletResponse
response) throws IOException
{
ServletConfig config = getServletConfig();
PrintWriter out = response.getWriter(); response.setContentType(“text/xml”);
try {
String sql = request.getParameter(“sql”); Statement
stat = connection.createStatement(); ResultSet rs = stat.executeQuery(sql);
ResultSetMetaData rsMeta = rs.getMetaData(); int rowNumber = 1;
Element root = new Element(“resultset”);
root.setAttribute(“query”, sql);
while
(rs.next()) {
Element row = new Element(“row”);
row.setAttribute(“index”,
Integer.toString(rowNumber));
int nCols = rsMeta.getColumnCount();
for
(int i=1; i<=nCols;
i+=1) {
String colName = rsMeta.getColumnName(i);
Element column = new Element(colName);
column.addContent(rs.getString(i));
row.addContent(column);
}
root.addContent(row); rowNumber += 1;
}
rs.close();
stat.close();
Document
doc = new
Document(root);
XMLOutputter outputter = new XMLOutputter(“\t”,
true);
outputter.output(doc, out);
} catch (Exception e) {
e.printStackTrace(out);
}
}
}
LISTING 7.20 XMLServlet Sample
Output
<?xml
version=”1.0”
encoding=”UTF-8”?>
<resultset query=”select *
from people where country like ‘united%’”> <row index=”1”>
<ID>220</ID>
<Country>United Arab Emirates</Country>
<Population>2369153.0</Population>
<GrowthRate>1.61</GrowthRate>
<BirthsPer1000>18.0</BirthsPer1000>
<DeathsPer1000>3.68</DeathsPer1000>
<NetMigrationPercent>1.82</NetMigrationPercent>
<NetMigration>43118.5846</NetMigration>
<InfantMortalityPer1000>17.17</InfantMortalityPer1000>
<TotalLifeExpectency>74.06</TotalLifeExpectency>
<MaleLifeExpectency>71.64</MaleLifeExpectency>
<FemaleLifeExpectency>76.61</FemaleLifeExpectency>
<TotalLiteracy>79.2</TotalLiteracy>
<MaleLiteracy>78.9</MaleLiteracy>
<FemalLiteracy>79.8</FemalLiteracy>
</row>
<row index=”2”>
<ID>221</ID>
<Country>United Kingdom</Country>
<Population>59511464.0</Population>
<GrowthRate>0.25</GrowthRate>
<BirthsPer1000>11.76</BirthsPer1000>
<DeathsPer1000>10.38</DeathsPer1000>
<NetMigrationPercent>1.07</NetMigrationPercent>
<NetMigration>636772.6648</NetMigration>
<InfantMortalityPer1000>5.63</InfantMortalityPer1000>
<TotalLifeExpectency>77.66</TotalLifeExpectency>
<MaleLifeExpectency>74.97</MaleLifeExpectency>
<FemaleLifeExpectency>80.49</FemaleLifeExpectency>
<TotalLiteracy>99.0</TotalLiteracy>
<MaleLiteracy>0.0</MaleLiteracy>
<FemalLiteracy>0.0</FemalLiteracy>
</row>
<row index=”3”>
<ID>222</ID>
<Country>United States</Country>
<Population>275562673.0</Population>
<GrowthRate>0.91</GrowthRate>
<BirthsPer1000>14.2</BirthsPer1000>
<DeathsPer1000>8.7</DeathsPer1000>
<NetMigrationPercent>3.5</NetMigrationPercent>
<NetMigration>9644693.555</NetMigration>
<InfantMortalityPer1000>6.82</InfantMortalityPer1000>
<TotalLifeExpectency>77.12</TotalLifeExpectency>
<MaleLifeExpectency>74.24</MaleLifeExpectency>
<FemaleLifeExpectency>79.9</FemaleLifeExpectency>
<TotalLiteracy>97.0</TotalLiteracy>
<MaleLiteracy>97.0</MaleLiteracy>
<FemalLiteracy>97.0</FemalLiteracy>
</row>
</resultset>
As in any
servlet, the bulk of the work is performed in the goGet()
method. The doGet() method will only be called
in response to a GET request. If responses to both
GET and POST requests are necessary,
you can override the service() method instead.
The SQL query
is supplied as part of the query string. We can obtain this string using the getParameter() method while supplying the name of the parameter. In our example, the
parameter name is simply sql. Once we have the SQL, we can issue standard
JDBC calls to obtain a result set.
Now the interesting part begins. We will use JDOM to create the DOM tree. As you saw earlier, JDOM can be easier to use when creating a document. The result set is translated into a DOM tree by using the ResultSet column names as element names. The column names are obtained through ResultSetMetaData. The resulting DOM tree is written to the response output stream using XMLOutputter.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.