Tải bản đầy đủ (.pdf) (71 trang)

Java 2 Bible Enterprise Edition phần 5 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (164.71 KB, 71 trang )

<head>
<title>
<xsl:value−of select="TITLE" />
</title>
</head>
<body>
<h1> <xsl:value−of select="TITLE" /> </h1>
<xsl:apply−templates select="ACT" />
</body>
</html>
</xsl:template>
<xsl:template match="ACT">
<xsl:apply−templates />
</xsl:template>
<xsl:template match="SCENE">
<xsl:apply−templates />
</xsl:template>
<xsl:template match="SPEECH">
<br />
<xsl:apply−templates />
</xsl:template>
<xsl:template match="SPEAKER">
<br />
<b> <xsl:apply−templates /> </b>
</xsl:template>
<xsl:template match="TITLE">
<br />
<h2> <xsl:apply−templates /> </h2>
</xsl:template>
<xsl:template match="STAGEDIR">
<br />


<em> <xsl:apply−templates /> </em>
</xsl:template>
<xsl:template match="LINE">
<br />
<xsl:apply−templates />
</xsl:template>
</xsl:stylesheet>
XLST transformations using JDOM
So far you've used JAXP to transform rich_iii.xml using program.xsl. You could also have written your Java
program using JDOM instead of JAXP. Just like the JAXP program Transform1, Transform2 is an adaptation
of CueMyLine5. In Chapter 13, you created this program to make changes to rich_iii.xml by using JDOM and
then saved the changes. You may need to refer back to Chapter 13 to review setting up your computer to run
JDOM applications. Here's a partial listing of the JDOM version of CueMyLine5.java:
package cue;
// imports
Chapter 14: Transforming and Binding Your XML Documents
276
public class CueMyLine5 {
Document document;
public CueMyLine5() {
try{
SAXBuilder builder = new SAXBuilder();
document = builder.build(new File("rich_iii.xml"));
} catch(JDOMException e){
System.out.println( "There's a JDOM problem.");
}
}
public void addPrologue(){ //
}
public void saveTheDocument(){

try{
XMLOutputter xmlOutputter = new XMLOutputter(" ", true);
xmlOutputter.setTextNormalize(true);
xmlOutputter.output(document,
new FileWriter("rewrite.xml"));
} catch (Exception e) {
System.out.println("Transformer Config Exception");
}
}
public static void main(String[] args) {
CueMyLine5 cueMyLine = new CueMyLine5();
cueMyLine.addPrologue();
cueMyLine.saveTheDocument();
}
}
As with Transform1, other than renaming the class and methods, you need to make surprisingly few
alterations. You will create Transform2.java in the change directory. You will parse rich_iii.xml and then
transform it using the XSLT style sheet program.xsl. Finally, you will output the transformed file as
JDOMalteredRichard.html. The differences between Transform2 and CueMyLine5 are shown in boldface in
the following code:
package change;
import org.jdom.input.SAXBuilder;
import org.jdom.Document;
import org.jdom.JDOMException;
import java.io.File;
import java.io.FileWriter;
import org.jdom.output.XMLOutputter;
import org.jdom.transform.JDOMSource;
import org.jdom.transform.JDOMResult;
import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
public class Transform2 {
Document document;
public Transform2() {
try{
SAXBuilder builder = new SAXBuilder();
Chapter 14: Transforming and Binding Your XML Documents
277
document = builder.build(new File("rich_iii.xml"));
} catch(JDOMException e){
System.out.println( "There's a JDOM problem.");
}
}
public void transformTheDocument(String stylesheet){
try {
Transformer transformer = TransformerFactory
.newInstance().newTransformer(
new StreamSource(stylesheet));
JDOMResult out = new JDOMResult();
transformer.transform(new JDOMSource(document), out);
XMLOutputter xmlOutputter = new XMLOutputter(" ", true);
xmlOutputter.setTextNormalize(true);
xmlOutputter.output(out.getDocument(),
new FileWriter("JDOMAlteredRichard.html"));
}
catch (TransformerException e) {
System.out.println("Transformer Exception");

}
catch (IOException e) {
System.out.println("IOException");
}
}
public static void main(String[] args) {
Transform2 transform2 = new Transform2();
transform2.transformTheDocument("program.xsl");
}
}
From now on, you can decide whether you prefer to use JAXP or JDOM to transform your XML.
Transforming XML
In the last section you looked at ways of presenting your XML files to human consumers. Often the clients for
your XML documents are other machines. You've seen that if you know the DTD for a given XML file then
you can easily write an application that extracts the information you need. If you are processing hundreds of
resumes each day, you may want to pre−screen the submissions to make certain that they meet some minimal
qualification before you hand−process them. Consider the difficulties that will arise when you interact with
another organization that is processing résumés that have been validated against a different DTD, one that the
organization has developed in−house.
In this section, you are concerned with transforming XML documents so that they can be read and understood.
This time, however, you aren't concerned with how they look but in how the data is structured. Although this
type of transformation is usually applied to data−centric XML documents, you can continue with the Richard
III example by converting a document that conforms to the existing play.dtd to a document that conforms to a
new DTD that you'll define.
Chapter 14: Transforming and Binding Your XML Documents
278
A second DTD for Shakespeare's plays
You've no doubt noticed by now that play.dtd defines elements but no attributes. There continue to be
arguments about what belongs in an attribute and what belongs in an element. At one extreme are those who
believe you should never use attributes. At the other are those who put anything they consider to be

non−displayable data in attributes. The DTD play.dtd takes the "never use attributes" approach. As a
reminder, here's play.dtd:
<!−− DTD for Shakespeare J. Bosak 1994.03.01, 1997.01.02 −−>
<!−− Revised for case sensitivity 1997.09.10 −−>
<!−− Revised for XML 1.0 conformity 1998.01.27 (thanks to Eve
Maler) −−>
<!−− <!ENTITY amp "&#38;#38;"> −−>
<!ELEMENT PLAY (TITLE, FM, PERSONAE, SCNDESCR, PLAYSUBT,
INDUCT?,PROLOGUE?, ACT+, EPILOGUE?)>
<!ELEMENT TITLE (#PCDATA)>
<!ELEMENT FM (P+)>
<!ELEMENT P (#PCDATA)>
<!ELEMENT PERSONAE (TITLE, (PERSONA | PGROUP)+)>
<!ELEMENT PGROUP (PERSONA+, GRPDESCR)>
<!ELEMENT PERSONA (#PCDATA)>
<!ELEMENT GRPDESCR (#PCDATA)>
<!ELEMENT SCNDESCR (#PCDATA)>
<!ELEMENT PLAYSUBT (#PCDATA)>
<!ELEMENT INDUCT (TITLE, SUBTITLE*,
(SCENE+|(SPEECH|STAGEDIR|SUBHEAD)+))>
<!ELEMENT ACT (TITLE, SUBTITLE*, PROLOGUE?, SCENE+,
EPILOGUE?)>
<!ELEMENT SCENE (TITLE, SUBTITLE*, (SPEECH | STAGEDIR |
SUBHEAD)+)>
<!ELEMENT PROLOGUE (TITLE, SUBTITLE*, (STAGEDIR | SPEECH)+)>
<!ELEMENT EPILOGUE (TITLE, SUBTITLE*, (STAGEDIR | SPEECH)+)>
<!ELEMENT SPEECH (SPEAKER+, (LINE | STAGEDIR | SUBHEAD)+)>
<!ELEMENT SPEAKER (#PCDATA)>
<!ELEMENT LINE (#PCDATA | STAGEDIR)*>
<!ELEMENT STAGEDIR (#PCDATA)>

<!ELEMENT SUBTITLE (#PCDATA)>
<!ELEMENT SUBHEAD (#PCDATA)>
Now it's time to create a second DTD for specifying one of Shakespeare's plays. This exercise is not intended
to suggest that this DTD needs improvements; the point of this section is to arrive at a different DTD. In the
next section, you'll construct an XSLT style sheet to convert rich_iii.xml into an XML document that
conforms to this new DTD.
The structure of a <SCENE>, <EPILOGUE>, and <PROLOGUE> are similar enough that you can treat them
all as if they were the same thing. The description of <ACT> in play.dtd restricts each act to having one or no
prologue, followed by at least one scene, followed by one or no epilogue. Although this new DTD can't
enforce this existing structure, the new DTD will only be used for Shakespeare's plays, none of which violate
this structure. Although it would be a bit more problematic, you can eliminate <INDUCT> and treat it as a
type of <SCENE>. Doing this will require you to revise the new DTD for plays with introductions that consist
of multiple scenes. For your purposes in this Richard III example, you can get away with the
oversimplification of the new DTD. A tradeoff is that the specification of <PLAY> will be a little less clear as
<scene> can refer to more than one type of element. The first optional <scene> is the introduction, the second
is the prologue, and the third is the epilogue.
Chapter 14: Transforming and Binding Your XML Documents
279
In the revised DTD, you can take advantage of a decision not to display the front matter or the list of
characters in the play. Elements such as <TITLE>, <SUBTITLE>, and <SPEAKER> are now treated as
attributes. Here's the new DTD, which you can save as newPlay.dtd (to avoid confusion later as to which DTD
is being discussed, this one uses lower case for all elements and attributes):
<!−− DTD example derived from the revised version of J. Bosak's
DTD for Shakespeare −−>
<!ELEMENT play (scndescr, scene?,scene?, act+, scene?)>
<!ELEMENT p (#PCDATA)>
<!ELEMENT scndescr (#PCDATA)>
<!ELEMENT act (scene+)>
<!ELEMENT scene ((speech | stagedir | subhead)+)>
<!ELEMENT speech ((line | stagedir | subhead)+)>

<!ELEMENT line (#PCDATA | stagedir)*>
<!ELEMENT stagedir (#PCDATA)>
<!ELEMENT subhead (#PCDATA)>
<!ATTLIST play title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST act title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST scene title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST speech speaker CDATA #REQUIRED >
Translating with a style sheet
You can think of the two DTDs as defining different dialects. Your next job is to provide the translation.
When Midwesterners refer to a carbonated beverage, they call it a pop. When they offer one to a New Yorker,
they have to ask if the New Yorker would like a soda if they wish to be understood. You could use an XSLT
style sheet to convert a <POP> element to a <SODA> element. Compare the two DTDs and look for ways in
which you might map the elements in play.dtd to the elements and attributes in newPlay.dtd.
Creating elements
Start by considering the simplest sort of map. A <LINE> as it is defined by play.dtd is exactly mapped to a
<line> as it is defined by newPlay.dtd. Whenever the translator encounters a <LINE> element, you want it to
create a <line> element and put the contents of <LINE> into the newly created <line>. Here's how you
arrange this:
<xsl:template match="LINE">
<xsl:element name="line">
<xsl:apply−templates />
</xsl:element>
</xsl:template>
The tag <xsl:element name="line"> creates the <line> element in the target file. The end tag for <line> will be
placed where the corresponding </xsl:element> tag and the <xsl:apply−templates /> tag will be replaced by
the contents of <LINE>. Suppose for a minute that you instead used this code:
<xsl:template match="LINE">

<xsl:element name="line">
</xsl:element>
</xsl:template>
Chapter 14: Transforming and Binding Your XML Documents
280
Because you haven't placed any content between the start and end <xsl:element> tags, the translator is smart
enough to replace these with the empty tag <line />.
Creating attributes
In play.dtd the element <SPEECH> had <SPEAKER> as a child element. In newPlay.dtd the element
<speech> has an attribute named speaker. In addition, <SPEECH> has <LINE>, <STAGEDIR>, and
<SUBHEAD> elements that need to be mapped across to the corresponding children of <speech>. You can do
this mapping with the following code:
<xsl:template match="SPEECH">
<xsl:element name="speech">
<xsl:attribute name="speaker">
<xsl:value−of select="SPEAKER" />
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>
Inside the <xsl:element> tags that map to the <speech> start and end tags, you include <xsl:attribute> tags that
specify the name of the attribute you are declaring as an attribute of the <xsl:attribute> tag. The contents of
the tag will be the value of the attribute: In this case the tag contains the value of the <SPEAKER> element.
Leaving elements out
Not all of the elements in play.dtd are being mapped over. For example, in the <PLAY> element, you won't
be keeping the front matter or the dramatis personae. You could specify this in either of two ways. The first
way is by explicitly listing the children of <PLAY> that you will keep in the target XML document, as
follows:
<xsl:template match="PLAY">
<xsl:element name="play">

<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="PLAYSUBT" />
</xsl:attribute>
<xsl:apply−templates select="SCNDESCR" />
<xsl:apply−templates select="INDUCT" />
<xsl:apply−templates select="PROLOGUE" />
<xsl:apply−templates select="ACT" />
<xsl:apply−templates select="EPILOGUE" />
</xsl:element>
</xsl:template>
An alternate approach with the same end result is to start by using the <xsl:apply−templates> to process all
the children of the <PLAY> element, like this:
<xsl:template match="PLAY">
<xsl:element name="play">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="PLAYSUBT" />
Chapter 14: Transforming and Binding Your XML Documents
281
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>
For any element that you don't want included in the resulting document, you can create an empty rule, like
this:

<xsl:template match="FM" />
Setting the document type declaration
When you were transforming rich_iii.xml into an HTML document, your XSLT style sheet included the
following element:
<xsl:output method="html" />
Now you are transforming one XML document into another one, so the value of method is now xml. Set the
doctype−system attribute to newPlay.dtd to point to your new DTD. Here's your new <output> element:
<xsl:output method="xml" doctype−system="newPlay.dtd" />
The XSLT translator will turn this input into the following output in alteredRichard.xml:
<!DOCTYPE play SYSTEM "newPlay.dtd">
You can also set the doctype−public attribute to include a PUBLIC declaration in your document type
declaration.
The complete style sheet
Now that you know how to construct the pieces of the style sheet, you can put them all together into a file
called translate.xsl, as shown in Listing 14−1.
Listing 14−1: The translate.xsl style sheet
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl=" >
<xsl:output method="xml" doctype−system="newPlay.dtd" />
<xsl:template match="PLAY">
<xsl:element name="play">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="PLAYSUBT" />
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>

<xsl:template match="ACT">
<xsl:element name="act">
<xsl:attribute name="title">
Chapter 14: Transforming and Binding Your XML Documents
282
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="SUBTITLE" />
</xsl:attribute>
<xsl:apply−templates select="PROLOGUE" />
<xsl:apply−templates select="SCENE" />
<xsl:apply−templates select="EPILOGUE" />
</xsl:element>
</xsl:template>
<xsl:template match="SCNDESCR">
<xsl:element name="scndescr">
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="INDUCT">
<xsl:element name="scene">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="SUBTITLE" />
</xsl:attribute>
<xsl:apply−templates/>
</xsl:element>

</xsl:template>
<xsl:template match="PROLOGUE">
<xsl:element name="scene">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="SUBTITLE" />
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="EPILOGUE">
<xsl:element name="scene">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="SUBTITLE" />
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="SCENE">
<xsl:element name="scene">
<xsl:attribute name="title">
<xsl:value−of select="TITLE" />
</xsl:attribute>
<xsl:attribute name="subtitle">
<xsl:value−of select="SUBTITLE" />

</xsl:attribute>
Chapter 14: Transforming and Binding Your XML Documents
283
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="SPEECH">
<xsl:element name="speech">
<xsl:attribute name="speaker">
<xsl:value−of select="SPEAKER" />
</xsl:attribute>
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="STAGEDIR">
<xsl:element name="stagedir">
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="LINE">
<xsl:element name="line">
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="P">
<xsl:element name="p">
<xsl:apply−templates />
</xsl:element>
</xsl:template>
<xsl:template match="TITLE" />

<xsl:template match="SUBTITLE" />
<xsl:template match="SPEAKER" />
<xsl:template match="PLAYSUBT" />
<xsl:template match="PERSONAE" />
<xsl:template match="FM" />
</xsl:stylesheet>
Adjust Transform1.java to use translate.xsl instead of program.xsl as the style sheet. You also need to change
the name of the output file from alteredRichard.html to alteredRichard.xml. Compile and run
Transform1.java. Figure 14−7 shows part of alteredRichard.xml viewed in IE 5.5.
Figure 14−7: The results of applying translate.xsl
Chapter 14: Transforming and Binding Your XML Documents
284
Binding with JAXB
Thus far in this chapter, you've looked at two types of transformations. You transformed elements of an XML
document into HTML so that you could easily display the resulting document in a browser. You also
transformed one XML document into another so an organization using a different DTD would be able to use
it. Now you will look at transforming XML into Java objects so that Java developers can use them more
easily. In this section, you'll create a binding schema and examine the generated files. In the next section,
you'll use this binding in a sample application.
The point of binding is that you are creating a mapping. This will enable you to more quickly translate data
stored in XML into Java objects, manipulate those objects, and then translate them back to persist them. As a
starting point, you'll transform the play.dtd that you've been working with to get a feel for what is created by
JAXB. Once you understand the default behavior of the schema compiler, you'll customize this behavior with
an example that features different datatypes.
Installing and running JAXB
Currently JAXB is available as an early−access release. You can download the distribution from
The release notes describe the installation on a UNIX platform.
There is no quick launcher for Windows, but you can easily run JAXB on a Windows box: Either copy the
two jar files in the lib directory of the distribution into your \jre\lib\ext directory, or change your
CLASSPATH to point to these two jars. Now you can run the schema compiler that generates the Java classes

from the command line.
In any case, you will need a binding schema that contains instructions for the schema compiler. At the very
minimum this binding schema must specify a root element. For example, the Shakespearean plays have a root
element of <PLAY>. Create a binding schema called Shakespeare.xjs that contains the following three lines:
<xml−java−binding−schema version="1.0ea">
<element name="PLAY" type="class" root="true" />
</xml−java−binding−schema>
Save the file in the same directory as play.dtd. Shakespeare.xjs has a root element called
<xml−java−binding−schema> with the version specified as early−access 1.0. For now, the only other content
of this file is the element <element> that has the name attribute set to the play.dtd element <PLAY> with
instructions that this element will become a class and that it can be the root element of a document. You can
specify that multiple elements can be root elements by using more than one <element> tag.
You can now use play.dtd to generate Java classes by using the schema compiler and passing it the name of
the DTD as well as the name of the binding schema. Open a command window and navigate to the directory
containing play.dtd and Shakespeare.xjs. Enter this command:
java com.sun.tools.xjc.Main "play.dtd" "Shakespeare.xjs"
This command runs the schema−compiler application and passes in two parameters. The first is for the
schema specified, in this case, by play.dtd. The second is for the binding schema Shakespeare.xjs. After a
moment you'll see feedback in the console window to let you know that .java files are being generated with
the names ACT.java, EPILOGUE.java, FM.java, INDUCT.java, LINE.java, PERSONAE.java,
PGROUP.java, PLAY.java, PROLOGUE.java, SCENE.java, and SPEECH.java.
Chapter 14: Transforming and Binding Your XML Documents
285
The generated code PLAY.java contains instance variables for each of the contained elements. Any of the
elements declared to contain #PCDATA are variables of type String in the generated Java code. The elements
(such as <PERSONAE>) that contain other elements become classes in their own right. This means that you
end up with a PERSONAE.java file, and that the PLAY class contains a variable named _PERSONAE of type
PERSONAE. You'll notice that, because play.dtd allows the element <PLAY> to contain one or more of the
<ACT> elements, the schema compiler generates a class named ACT.java and the PLAY class contains a List
named _ACT created using a utility inner class called PredicatedLists. Listing 14−2 shows a piece of the

generated source code.
Listing 14−2: Play class source code (excerpt)
// many imports
public class PLAY extends MarshallableRootElement
implements RootElement {
private String _TITLE;
private FM _FM;
private PERSONAE _PERSONAE;
private String _SCNDESCR;
private String _PLAYSUBT;
private INDUCT _INDUCT;
private PROLOGUE _PROLOGUE;
private List _ACT = PredicatedLists.createInvalidating(
this, new ACTPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_ACT =
new ACTPredicate();
private EPILOGUE _EPILOGUE;
// accessor methods begin
public String getTITLE() { }
public void setTITLE(String _TITLE) { }
// accessors for FM, PERSONAE, SCNDESCR,PLAYSUBT, INDUCT,
// PROLOGUE, EPILOGUE are same. ACT is different it's a List.
//methods for validation
public void validateThis()
throws LocalValidationException { }
public void validate(Validator v)
throws StructureValidationException { }
// marshal methods turn content trees into XML documents
public void marshal(Marshaller m) throws IOException { }
public void unmarshal(Unmarshaller u)

throws UnmarshalException { }
// unmarshal methods turn XML files into Java objects
public static PLAY unmarshal(InputStream in)
throws UnmarshalException { }
public static PLAY unmarshal(XMLScanner xs)
throws UnmarshalException { }
public static PLAY unmarshal(XMLScanner xs, Dispatcher d)
Chapter 14: Transforming and Binding Your XML Documents
286
throws UnmarshalException { }
// customized methods for equals() hashCode() and toString()
public boolean equals(Object ob) { }
public int hashCode() { }
public String toString() { }
public static Dispatcher newDispatcher() { }
// inner class
private static class ACTPredicate
implements PredicatedLists.Predicate { }
}
In the next section you'll begin an example that will give you better control over the generated code.
Introducing the user−stories example
One set of artifacts from the extreme programming (XP) methodology is user stories. These small descriptions
of how a user will interact with the application are traditionally kept on index cards so that they can easily be
sorted, added to, and, if necessary, torn in half. What follows is not a recommendation to store user stories
electronically, but simply a manageable and easily understood example of specifying the type of the data
being turned into Java objects.
In this example a user story will consist of an identifying number along with a name, a description, and an
estimate of how long the story might take to complete. In XP a user story is written by the customer.
Programmers can break the story down into tasks they will need to perform in order to deliver the
functionality described in the story. A task will again have some sort of identifying number, name,

description, and estimate. A task will also have a programmer assigned to it. If you want to track the
programmer's velocity, you may want to include the actual time the programmer spends on the task.
The DTD for user stories
Here's UserStories.dtd, a possible DTD for user stories.
<!ELEMENT stories (userStory)* >
<!ELEMENT userStory (idNumber, name, description, task*,
estimate) >
<!ELEMENT task (idNumber, name, description, programmer,
estimate, actual?) >
<!ELEMENT idNumber (#PCDATA) >
<!ELEMENT name (#PCDATA) >
<!ELEMENT description (#PCDATA) >
<!ELEMENT estimate (#PCDATA) >
<!ELEMENT programmer (#PCDATA) >
<!ELEMENT actual (#PCDATA) >
<!ATTLIST userStory isCompleted CDATA "false" >
<!ATTLIST task isCompleted CDATA "false"
dateAssigned CDATA #REQUIRED >
A minimal binding schema for user stories
You'll need to start with a minimal binding schema that you can save as UserStories.xjs in the same directory
as your XML document. You can build in some flexibility by specifying that the root of a tree can be either
Chapter 14: Transforming and Binding Your XML Documents
287
the <stories> element or the <userStory> element. Here's the starting point for UserStories.xjs:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" />
</xml−java−binding−schema>
Use the schema compiler to generate the Java classes with the following command:
java com.sun.tools.xjc.Main "UserStories.dtd" "UserStories.xjs"

This command causes the source files Stories.java, Task.java, and UserStory.java to be generated.
Refining the binding schema
One of the benefits of programming in Java is that it is a strongly typed language. The more accurately you
can specify the types of the variables you are using, the more the compiler and runtime can help you. In
Chapter 11 you saw that XML Schemas enable you to more precisely specify the datatypes. Unfortunately,
JAXB can't yet work with XML Schemas, and so you have to add the information in the binding schema.
Assigning an element a primitive type
In the current example, you might prefer that an <idNumber> be a number and not just a String. Look at the
relevant code generated by default for UserStory.java. The variable idNumber is a String:
private String _IdNumber;
public String getIdNumber() {
return _IdNumber;
}
public void setIdNumber(String _IdNumber) {
this._IdNumber = _IdNumber;
if (_IdNumber == null) {
invalidate();
}
}
You can add the following line to the binding schema to specify that <idNumber> should be treated as an int:
<element name="idNumber" type="value" convert="int" />
As a result the generated variable _IdNumber is now of type int and the accessor methods are also changed.
Notice in the following code that the changes include the addition of a boolean flag for the existence of
_IdNumber. An exception is now thrown if getIdNumber() is called when has_IdNumber is false.
private int _IdNumber;
private boolean has_IdNumber = false;
public int getIdNumber() {
if (has_IdNumber) {
return _IdNumber;
}

throw new NoValueException("idNumber");
}
Chapter 14: Transforming and Binding Your XML Documents
288
public void setIdNumber(int _IdNumber) {
this._IdNumber = _IdNumber;
has_IdNumber = true;
invalidate();
}
public boolean hasIdNumber() {
return has_IdNumber;
}
public void deleteIdNumber() {
has_IdNumber = false;
invalidate();
}
As expected, the same changes are made to the class Task.java.
Assigning an element a non−primitive type
You must perform an extra step in order to assign a non−primitive type to an element. You have to specify a
name for the type you're converting to, and then use a <conversion> element to specify the actual type. This
sounds more confusing than it is. To treat the variable _IdNumber as an Integer, you can change
UserStories.xjs to the following:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" />
<element name="idNumber" type="value"
convert="DummyPlaceholder" />
<conversion name="DummyPlaceholder"
type="java.util.Integer" />
</xml−java−binding−schema>

As a general coding practice, you should choose a more descriptive name (Integer perhaps) for your
DummyPlaceholder. Run then preceding code through the schema compiler and your _IdNumber will have
type Integer.
Working with attributes
When working with elements you can make a single change to the element <idNumber> and change the type
of the generated variable _IdNumber in both Task.java and UserStory.java. Now consider the attribute
isCompleted. It makes more sense to treat isCompleted as a boolean than as a String. Unlike with elements,
however, you have to make this change twice: once for the isCompleted attribute of the element <task> and
once for the isCompleted attribute of the element <userStory>.
The resulting binding schema now looks like this:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" >
<attribute name="isCompleted" convert="boolean"/>
</element>
<element name="task" type="class" >
<attribute name="isCompleted" convert="boolean" />
</element>
<element name="idNumber" type="value" convert="int" />
</xml−java−binding−schema>
Chapter 14: Transforming and Binding Your XML Documents
289
In Task.java the following variables and methods are related to the attribute isCompleted:
private boolean _IsCompleted;
private boolean isDefaulted_IsCompleted = true;
private final static boolean DEFAULT_ISCOMPLETED = false;
public boolean defaultedIsCompleted() {
return isDefaulted_IsCompleted;
}
public boolean getIsCompleted() {

if (!isDefaulted_IsCompleted) {
return _IsCompleted;
}
return DEFAULT_ISCOMPLETED;
}
public void setIsCompleted(boolean _IsCompleted) {
this._IsCompleted = _IsCompleted;
isDefaulted_IsCompleted = false;
invalidate();
}
public boolean hasIsCompleted() {
return true;
}
public void deleteIsCompleted() {
isDefaulted_IsCompleted = true;
invalidate();
}
Recall that in the DTD you set the default value of isCompleted to false. This would also be the default value
for a boolean instance variable. The schema compiler isn't quite so sophisticated. It makes sure that the default
value is stored in the constant DEFAULT_ISCOMPLETED. It then adds the flag isDefaulted_IsCompleted
and sets it to true. You can change these entries by making changes to the DTD. For example, you can replace
false with #REQUIRED or, more simply, with true.
Handling multiple occurrences
A user story may have more than one task associated with it. Change UserStories.dtd so that a <userStory>
can contain only one <task> by removing the asterisk following task, like this:
<!ELEMENT userStory (idNumber, name, description, task,
estimate) >
Use the schema compiler to once again generate Stories.java, Task.java, and UserStory.java. You can see that
the items in UserStory.java that correspond to the element <task> are the following:
private Task _Task;

public Task getTask() {
return _Task;
}
public void setTask(Task _Task) {
this._Task = _Task;
if (_Task == null) {
Chapter 14: Transforming and Binding Your XML Documents
290
invalidate();
}
}
This code looks like what you expect in a standard Java datatype. The variable _Task is of type Task and is
accompanied by the getTask() and setTask () methods. Now you can investigate what changes when <task>
can occur more than one time. Re−edit UserStory.dtd to replace the asterisk after the word task, like this:
<!ELEMENT userStory (idNumber, name, description, task*,
estimate) >
Use the schema compiler to again generate the Java source files. You'll see quite a few differences in the
relevant code for the element <task> from UserStory.java than for the code you just examined.
The first difference is that the variable _Task is not of type Task but is instead a List created using an inner
class called TaskPredicate that implements the interface PredicatedLists. Here's the relevant code snippet.
private List _Task = PredicatedLists.createInvalidating(this,
new TaskPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_Task = new
TaskPredicate();
private static class TaskPredicate
implements PredicatedLists.Predicate {
public void check(Object ob) {
if (!(ob instanceof Task)) {
throw new InvalidContentObjectException(ob,
(Task.class));

}
}
}
Notice that the accessors are available only to get the entire List named _Task, to delete the List by assigning
null to the pointer, and to empty the list. No setTask() method is available.
public List getTask() {
return _Task;
}
public void deleteTask() {
_Task = null;
invalidate();
}
public void emptyTask() {
_Task = PredicatedLists.createInvalidating(this, pred_Task,
new ArrayList());
}
A final version of the binding schema
The elements <estimate> and <actual> refer to time. If you think in terms of ideal engineering hours, this
should be good enough for the purposes of this example. So convert the types generated by these two
elements to ints. The attribute dateAssigned should be a date. The final binding schema could look like this:
<xml−java−binding−schema version="1.0ea">
Chapter 14: Transforming and Binding Your XML Documents
291
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" >
<attribute name="isCompleted" convert="boolean"/>
</element>
<element name="task" type="class" >
<attribute name="isCompleted" convert="boolean" />
<attribute name="dateAssigned" convert="Date" />

</element>
<element name="idNumber" type="value" convert="int" />
<element name="estimate" type="value" convert="int" />
<element name="actual" type="value" convert="int" />
<conversion name="Date" type="java.util.Date" />
</xml−java−binding−schema>
The file UserStory.java
The files generated by the schema compiler tend to be quite long, and so there's no point in listing all of them.
It is, however, instructive to look at one of them. UserStory.java, shown in Listing 14−3, includes all the
features you've seen already in this chapter and, in addition, contains code for functionality such as validating,
marshalling, and unmarshalling, which you'll use in the next section.
Listing 14−3: UserStory.java
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.bind.ConversionException;
import javax.xml.bind.Dispatcher;
import javax.xml.bind.DuplicateAttributeException;
import javax.xml.bind.InvalidAttributeException;
import javax.xml.bind.InvalidContentObjectException;
import javax.xml.bind.LocalValidationException;
import javax.xml.bind.MarshallableObject;
import javax.xml.bind.MarshallableRootElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.MissingContentException;
import javax.xml.bind.NoValueException;
import javax.xml.bind.PredicatedLists;
import javax.xml.bind.PredicatedLists.Predicate;

import javax.xml.bind.RootElement;
import javax.xml.bind.StructureValidationException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidatableObject;
import javax.xml.bind.Validator;
import javax.xml.marshal.XMLScanner;
import javax.xml.marshal.XMLWriter;
public class UserStory
extends MarshallableRootElement
implements RootElement
{
private boolean _IsCompleted;
private boolean isDefaulted_IsCompleted = true;
private final static boolean DEFAULT_ISCOMPLETED = false;
Chapter 14: Transforming and Binding Your XML Documents
292
private int _IdNumber;
private boolean has_IdNumber = false;
private String _Name;
private String _Description;
private List _Task = PredicatedLists.createInvalidating(
this, new TaskPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_Task = new
TaskPredicate();
private int _Estimate;
private boolean has_Estimate = false;
public boolean defaultedIsCompleted() {
return isDefaulted_IsCompleted;
}

public boolean getIsCompleted() {
if (!isDefaulted_IsCompleted) {
return _IsCompleted;
}
return DEFAULT_ISCOMPLETED;
}
public void setIsCompleted(boolean _IsCompleted) {
this._IsCompleted = _IsCompleted;
isDefaulted_IsCompleted = false;
invalidate();
}
public boolean hasIsCompleted() {
return true;
}
public void deleteIsCompleted() {
isDefaulted_IsCompleted = true;
invalidate();
}
public int getIdNumber() {
if (has_IdNumber) {
return _IdNumber;
}
throw new NoValueException("idNumber");
}
public void setIdNumber(int _IdNumber) {
this._IdNumber = _IdNumber;
has_IdNumber = true;
invalidate();
}
public boolean hasIdNumber() {

return has_IdNumber;
}
public void deleteIdNumber() {
has_IdNumber = false;
invalidate();
}
public String getName() {
return _Name;
Chapter 14: Transforming and Binding Your XML Documents
293
}
public void setName(String _Name) {
this._Name = _Name;
if (_Name == null) {
invalidate();
}
}
public String getDescription() {
return _Description;
}
public void setDescription(String _Description) {
this._Description = _Description;
if (_Description == null) {
invalidate();
}
}
public List getTask() {
return _Task;
}
public void deleteTask() {

_Task = null;
invalidate();
}
public void emptyTask() {
_Task = PredicatedLists.createInvalidating(this,
pred_Task, new ArrayList());
}
public int getEstimate() {
if (has_Estimate) {
return _Estimate;
}
throw new NoValueException("estimate");
}
public void setEstimate(int _Estimate) {
this._Estimate = _Estimate;
has_Estimate = true;
invalidate();
}
public boolean hasEstimate() {
return has_Estimate;
}
public void deleteEstimate() {
has_Estimate = false;
invalidate();
}
public void validateThis()
throws LocalValidationException
{
if (!has_IdNumber) {
throw new MissingContentException("idNumber");

Chapter 14: Transforming and Binding Your XML Documents
294
}
if (_Name == null) {
throw new MissingContentException("name");
}
if (_Description == null) {
throw new MissingContentException("description");
}
if (!has_Estimate) {
throw new MissingContentException("estimate");
}
}
public void validate(Validator v)
throws StructureValidationException
{
for (Iterator i = _Task.iterator(); i.hasNext(); ) {
v.validate(((ValidatableObject) i.next()));
}
}
public void marshal(Marshaller m)
throws IOException
{
XMLWriter w = m.writer();
w.start("userStory");
if (!isDefaulted_IsCompleted) {
w.attribute("isCompleted",
printBoolean(_IsCompleted));
}
if (has_IdNumber) {

w.leaf("idNumber", Integer.toString(_IdNumber));
}
w.leaf("name", _Name.toString());
w.leaf("description", _Description.toString());
if (_Task.size()> 0) {
for (Iterator i = _Task.iterator(); i.hasNext(); ) {
m.marshal(((MarshallableObject) i.next()));
}
}
if (has_Estimate) {
w.leaf("estimate", Integer.toString(_Estimate));
}
w.end("userStory");
}
private static String printBoolean(boolean f) {
return (f?"true":"false");
}
public void unmarshal(Unmarshaller u)
throws UnmarshalException
{
XMLScanner xs = u.scanner();
Validator v = u.validator();
xs.takeStart("userStory");
while (xs.atAttribute()) {
String an = xs.takeAttributeName();
if (an.equals("isCompleted")) {
if (!isDefaulted_IsCompleted) {
throw new DuplicateAttributeException(an);
}
Chapter 14: Transforming and Binding Your XML Documents

295
try {
_IsCompleted =
readBoolean(xs.takeAttributeValue());
} catch (Exception x) {
throw new ConversionException(an, x);
}
isDefaulted_IsCompleted = false;
continue;
}
throw new InvalidAttributeException(an);
}
{
xs.takeStart("idNumber");
String s;
if (xs.atChars(XMLScanner.WS_COLLAPSE)) {
s = xs.takeChars(XMLScanner.WS_COLLAPSE);
} else {
s = "";
}
try {
_IdNumber = Integer.parseInt(s);
} catch (Exception x) {
throw new ConversionException("idNumber", x);
}
has_IdNumber = true;
xs.takeEnd("idNumber");
}
if (xs.atStart("name")) {
xs.takeStart("name");

String s;
if (xs.atChars(XMLScanner.WS_COLLAPSE)) {
s = xs.takeChars(XMLScanner.WS_COLLAPSE);
} else {
s = "";
}
try {
_Name = String.valueOf(s);
} catch (Exception x) {
throw new ConversionException("name", x);
}
xs.takeEnd("name");
}
if (xs.atStart("description")) {
xs.takeStart("description");
String s;
if (xs.atChars(XMLScanner.WS_COLLAPSE)) {
s = xs.takeChars(XMLScanner.WS_COLLAPSE);
} else {
s = "";
}
try {
_Description = String.valueOf(s);
} catch (Exception x) {
throw new ConversionException(
"description", x);
}
xs.takeEnd("description");
}
{

List l = PredicatedLists.create(this, pred_Task,
new ArrayList());
Chapter 14: Transforming and Binding Your XML Documents
296
while (xs.atStart("task")) {
l.add(((Task) u.unmarshal()));
}
_Task = PredicatedLists.createInvalidating(this,
pred_Task, l);
}
{
xs.takeStart("estimate");
String s;
if (xs.atChars(XMLScanner.WS_COLLAPSE)) {
s = xs.takeChars(XMLScanner.WS_COLLAPSE);
} else {
s = "";
}
try {
_Estimate = Integer.parseInt(s);
} catch (Exception x) {
throw new ConversionException("estimate", x);
}
has_Estimate = true;
xs.takeEnd("estimate");
}
xs.takeEnd("userStory");
}
private static boolean readBoolean(String s)
throws ConversionException

{
if (s.equals("true")) {
return true;
}
if (s.equals("false")) {
return false;
}
throw new ConversionException(s);
}
public static UserStory unmarshal(InputStream in)
throws UnmarshalException
{
return unmarshal(XMLScanner.open(in));
}
public static UserStory unmarshal(XMLScanner xs)
throws UnmarshalException
{
return unmarshal(xs, newDispatcher());
}
public static UserStory unmarshal(XMLScanner xs,
Dispatcher d)
throws UnmarshalException
{
return ((UserStory) d.unmarshal(xs,
(UserStory.class)));
}
public boolean equals(Object ob) {
if (this == ob) {
return true;
}

Chapter 14: Transforming and Binding Your XML Documents
297
if (!(ob instanceof UserStory)) {
return false;
}
UserStory tob = ((UserStory) ob);
if (!isDefaulted_IsCompleted) {
if (tob.isDefaulted_IsCompleted) {
return false;
}
if (_IsCompleted!= tob._IsCompleted) {
return false;
}
} else {
if (!tob.isDefaulted_IsCompleted) {
return false;
}
}
if (has_IdNumber) {
if (!tob.has_IdNumber) {
return false;
}
if (_IdNumber!= tob._IdNumber) {
return false;
}
} else {
if (tob.has_IdNumber) {
return false;
}
}

if (_Name!= null) {
if (tob._Name == null) {
return false;
}
if (!_Name.equals(tob._Name)) {
return false;
}
} else {
if (tob._Name!= null) {
return false;
}
}
if (_Description!= null) {
if (tob._Description == null) {
return false;
}
if (!_Description.equals(tob._Description)) {
return false;
}
} else {
if (tob._Description!= null) {
return false;
}
}
if (_Task!= null) {
if (tob._Task == null) {
return false;
}
if (!_Task.equals(tob._Task)) {
return false;

}
} else {
if (tob._Task!= null) {
Chapter 14: Transforming and Binding Your XML Documents
298
return false;
}
}
if (has_Estimate) {
if (!tob.has_Estimate) {
return false;
}
if (_Estimate!= tob._Estimate) {
return false;
}
} else {
if (tob.has_Estimate) {
return false;
}
}
return true;
}
public int hashCode() {
int h = 0;
h = ((31 *h)+(_IsCompleted? 137 : 139));
h = ((67 *h)+(isDefaulted_IsCompleted? 59 : 61));
h = ((31 *h)+ _IdNumber);
h = ((127 *h)+((_Name!= null)?_Name.hashCode(): 0));
h = ((127 *h)+((_Description!=
null)?_Description.hashCode(): 0));

h = ((127 *h)+((_Task!= null)?_Task.hashCode(): 0));
h = ((31 *h)+ _Estimate);
return h;
}
public String toString() {
StringBuffer sb = new StringBuffer("<<userStory");
sb.append(" isCompleted=");
sb.append(printBoolean(getIsCompleted()));
if (has_IdNumber) {
sb.append(" idNumber=");
sb.append(Integer.toString(_IdNumber));
}
if (_Name!= null) {
sb.append(" name=");
sb.append(_Name.toString());
}
if (_Description!= null) {
sb.append(" description=");
sb.append(_Description.toString());
}
if (_Task!= null) {
sb.append(" task=");
sb.append(_Task.toString());
}
if (has_Estimate) {
sb.append(" estimate=");
sb.append(Integer.toString(_Estimate));
}
sb.append(">>");
return sb.toString();

}
public static Dispatcher newDispatcher() {
return Stories.newDispatcher();
Chapter 14: Transforming and Binding Your XML Documents
299
}
private static class TaskPredicate
implements PredicatedLists.Predicate
{
public void check(Object ob) {
if (!(ob instanceof Task)) {
throw new InvalidContentObjectException(ob, (Task.class));
}
}
}
}
Using the JAXB Bindings
You can think of DTDs and Java classes as having the following correspondence. Java classes are used to
produce objects; you can think of a class as a template for an object. In the same way, a valid XML document
corresponds to a schema (in this case a DTD). In the preceding section, you created Java classes from a DTD
so that you now have a mapping of what you can think of as templates. In this section, you'll work with the
instances of the DTD and these classes. You will take an XML file and convert it to a Java object or create
from scratch a Java object that could have come from a valid XML file. Conversely, you will save the Java
object as a valid XML file. You'll look at both procedures in this section.
Unmarshalling: Java objects from XML documents
In this section, you'll begin to look at the process of serializing and deserializing. In this context, you will
refer to this process as marshalling and unmarshalling. Start with this valid XML document called
SampleStories.xml, which you can use to create Java objects:
<?xml version="1.0" encoding="UTF−8"?>
<!DOCTYPE userStory SYSTEM "UserStories.dtd">

<userStory isCompleted="false">
<idNumber> 7 </idNumber>
<name> Make Lunch </name>
<description> Prepare something to eat. </description>
<estimate> 1 </estimate>
</userStory>
You can convert this document to a Java object by calling the unmarshal() method with the signature shown in
this snippet:
public static UserStory unmarshal(InputStream in)
This method calls the unmarshal() method in javax.xml.bind.Dispatcher, which in turn builds the content tree
and then validates it against the DTD that the schema compiler uses to generate the Java classes.
In your custom application, you will use the static unmarshal() method in an instance of UserStory. You will
pass the method a FileInputStream constructed with the File constructed from your XML document. In the
following small example, you can see how the content tree is built from the file SampleStories.xml and then
Chapter 14: Transforming and Binding Your XML Documents
300

×