14_588451 ch08.qxd 4/15/05 12:23 AM Page 113
In general, do what works. Let the XML documents or data that you work with drive you toward or away from creating formal document descriptions.
Our experience has been that any application that involves more than a one- time or throwaway document or data collection is worthy of its own formal description (or at least, customization or outright use of an existing standard DTD). Because XML’s rules let you skip the document description if you like, you may certainly decide otherwise.
Inspecting the XML Prolog
In order to use a DTD with your XML document, you need to add a DOCTYPE declaration to your document — and the XML prolog is where you put it.
The XML prologis the first thing that a processor — or human eye, for that matter — sees in an XML document. You place it at the top of your XML docu- ment, and it describes the document’s content and structure.
An XML prolog may include the following items:
XML declaration DOCTYPEdeclaration Comments
Processing instructions White space
Notice the phrase may include.An XML prolog doesn’t have to include any of that information — but Listing 8-1shows an XML prolog that does.
Listing 8-1: An XML Prolog
<!-- Beginning of Prolog -->
<?xml version=”1.0” encoding=”UTF-8” standalone=”no”?>
<!DOCTYPE books SYSTEM “bookstore.dtd”>
<!-- End of Prolog -->
<!-- Beginning of Document Body -->
<books>
. ..
</books>
<!-- End of Document Body -->
114 Part III: Building In Validation with DTDs and Schemas
14_588451 ch08.qxd 4/15/05 12:23 AM Page 114
Take a second to look at what we include in the prolog:
The first line is the XML declaration.
The second line invokes a specific DOCTYPEdeclaration named books.
The next two lines are comments that denote the end of the prolog and the beginning of the document proper.
Examining the XML declaration
Generally speaking, a declarationis markup that tells an XML processor what to do. Declarations don’t add structure or define document elements.
Instead, they provide instructions for a processor, such as what type of docu- ment to process and what standards to use.
As you discover in Chapter 5, the XML declaration can include version, encoding, and/or standaloneattributes:
<?xml version=”1.0” encoding=”UTF-8” standalone=”no”?>
This statement tells the processor some essential stuff:
This is an XML document.
The version of XML is XML 1.0.
The character encoding is UTF-8.
An external document may be needed to complete the document con- tent (standalone=”no”).
DTDs can be internal (included within an XML document itself) or a separate external document. If we include a standaloneattribute in our XML declara- tion, standalone=”yes” implies that the document doesn’t rely on markup declarations defined in an external document — such as an external DTD — but could include an internal DTD. If standaloneis set to equal “no”, or if you don’t include a standaloneattribute, it leaves the issue unresolved — translation, it may or may not reference one or more external DTDs.
If you’re not sure whether or not to include a standaloneattribute, leave it out. The default value is standalone=”no”, so the XML processor will load whatever documents it needs.
115
Chapter 8: Understanding and Using DTDs
14_588451 ch08.qxd 4/15/05 12:23 AM Page 115
Discovering the DOCTYPE
The document type (DOCTYPE) declarationis markup that tells the processor where it can find a specific DTD. In other words, a DOCTYPEdeclaration links an XML document to a corresponding DTD. Please also note that the DOCTYPE declaration is an SGML construct and, therefore, follows SGML syntax and not XML syntax. This explains why only some values appear in quotes in this statement.
While you read this chapter, don’t confuse document type (DOCTYPE) declara- tions with Document Type Definitions (DTDs). The DOCTYPEdeclaration is the locator— it simply tells the processor where to find the DTD.
Here’s the basic markup of a DOCTYPEdeclaration:
<!DOCTYPE books SYSTEM “bookstore.dtd”>
<!DOCTYPEmarks the start of the DOCTYPEdeclaration. booksis the name of the DTD used. SYSTEM “bookstore.dtd”tells the processor to fetch an external document — in this case, a file named bookstore.dtd.
In the preceding example, bookstore.dtdis a relative Uniform Resource Identifier (URI). URIs are basically filenames — in effect, locations. bookstore.
dtdpoints to an external DTD that resides in the same folder as the XML doc- ument but not in the same document. We delve into how to reference exter- nal DTDs in the “Calling a DTD” section later in this chapter. (Hint:You might notice the resemblance between the terms URLand URI. No accident: A URL is a type of URI.)
Understanding comments
Comments — use them and read them! An author (yes, we mean you) can use comments to include text that explains a document better (humans love that sort of thing) without that text being displayed — or even processed. The syntax — the same as for HTML comments, because HTML is built on SGML — looks like this:
<!-- comment text -->
Comments are like an owner’s manual; they can help you find your way through a document when something breaks down or when you need to make changes. Use them liberally, but know why and how to use them!
As long as you follow the correct format, comments remain visible only when you’re viewing the markup itself. If you don’t follow the correct format, though, parts of your comments may show up when users view your docu- ment ,or your document may not display correctly. The correct format is:
116 Part III: Building In Validation with DTDs and Schemas
14_588451 ch08.qxd 4/15/05 12:23 AM Page 116
<!-- Include your comment here -->
You have two rules to live by when you’re using comments:
Never nest a comment inside another element.
Never include - (hyphen) or -- (double hyphen) withinthe comment text.
Those characters might confuse processors into thinking that you’re closing the comment — which means they’d end up treating the remain- ing comment content as a syntax error!
Processing instructions
Using comments enables you to leave human-style instructions (that is, com- ments) addressed to someone who reads the markup without disrupting the document’s structure. Processing instructionsare like comments addressed to machines; they provide a way to send instructions to computer programs or applications.
All processing instructions follow this format:
<?name data?>
A common example of a processing instruction in XML documents is a refer- ence to stylesheets. For example, in the following processing instruction
<?xml:stylesheet type=”text/css” href=”bookstore.css”?>
the name is xml:stylesheet, and the data is type=”text/css”and href=”bookstore.css”. If the processor recognizes the name, the data is used — otherwise, it’s ignored.
All processing instructions must begin with <?and end with ?>.
How about that white space?
Any document has places where writing is and places where writing isn’t — but in an XML document, many of the places that look blank are actually white space — nonprinting characters such as spaces, tabs, carriage returns, or line feeds. The XML specification allows you to add white space outside markup; it’s ignored when the document is processed.
Think of it this way: If we wrote a book without paragraph breaks, readers would give up reading after a few pages. A line of white space between para- graphs (that is, a break) is easier on the eyes. The same logic applies to XML
117
Chapter 8: Understanding and Using DTDs
14_588451 ch08.qxd 4/15/05 12:23 AM Page 117
documents. When you write markup, consider adding a line of white space between sections. That way, when someone reads your XML document, he or she can read it without a hitch.
Some elements treat white space in a special way. Including white space out- sideXML elements is safe, but do your homework before you add extra white space insidean element. If you find yourself intrigued by the use of white space, read up on the xml:spaceattribute, which lets applications know when white space matters and when it doesn’t. For more information on the xml:spaceattribute, check out the W3C site at www.w3.org/TR/
REC-xml#sec-white-space.
The preceding section on the XML prolog refers to your XML document — not to a DTD. A DTD may include an XML declaration and comments, but those are optional — and a DTD is not required to have a prolog.
Reading a DTD
Even if you don’t plan to create your own DTDs from scratch, knowing how to read them is helpful. In theory — and we hope in practice — XML (and DTDs) should be easy to read and understand. You should be able to look at a DTD, list all elements and their attributes, and understand how and when to use those elements and attributes.
Create a document tree to help you better understand the hierarchy of docu- ment elements. A document tree begins with one root element. All other ele- ments are children of (in other words, nest within) that root element.
In the following sections, we dissect the bookstore DTD, shown in Listing 8-2.
You need to get your mind around all the pieces and parts of a DTD before you try to create one yourself. (If you already recognize all the pieces of a DTD, feel free to move on over to Chapter 9 to find out more about XML schemas.)
DTDs aren’t written in XML — they’re written in SGML and follow SGML rules. The DTD terms must be used exactly as written below; in other words,
!ELEMENT, !ATTLIST, #REQUIRED, #PCDATA, and EMPTYmust all be capital- ized. If you change the case, your DTD won’t work.
Listing 8-2: The bookstore DTD, External Version
<?xml version=”1.0” encoding=”UTF-8”?>
<!ELEMENT books (book+, totalCost, customer)>
<!ELEMENT book (bookInfo, salesInfo)>
<!ATTLIST book contentType (Fiction | Nonfiction) #REQUIRED format (Hardback | Paperback) #REQUIRED>
118 Part III: Building In Validation with DTDs and Schemas
14_588451 ch08.qxd 4/15/05 12:23 AM Page 118
<!ELEMENT bookInfo (title, author, publisher, isbn)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
<!ELEMENT isbn (#PCDATA)>
<!ELEMENT salesInfo (price, itemNumber, date, source, shipping, cost)>
<!ELEMENT price (#PCDATA)>
<!ATTLIST price priceType (Retail | Wholesale) #REQUIRED>
<!ELEMENT itemNumber (#PCDATA)>
<!ELEMENT date (#PCDATA)>
<!ELEMENT source EMPTY>
<!ATTLIST source sourceType (Retail | Wholesale) #REQUIRED>
<!ELEMENT shipping (#PCDATA)>
<!ELEMENT cost (#PCDATA)>
<!ELEMENT totalCost (#PCDATA)>
<!ELEMENT customer (custNumber, lastName, firstName, address, city, state, zip, phone, email)>
<!ATTLIST customer custType (newRetail | prevRetail | newWholesale | prevWholesale) #REQUIRED>
<!ELEMENT custNumber (#PCDATA)>
<!ELEMENT lastName (#PCDATA)>
<!ELEMENT firstName (#PCDATA)>
<!ELEMENT address (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT email (#PCDATA)>
Using Element Declarations
Because the heart of an XML document is made up of its elements, you must define them in your DTD. To do so, you use element type declarations.
Element type declarations are important because they not only name your elements, but also define any children (nested elements) that an element might have.
We start with the root element for a document based on our example DTD:
<books>
. . .
</books>
All other elements occur inside the root (if they’re not more deeply nested), and all other elements relate back to the root somehow. Therefore, the root element is topmost in a document’s hierarchy of elements. Part of what makes XML so great is that the element hierarchy is logical and easy to read or understand.
119
Chapter 8: Understanding and Using DTDs
14_588451 ch08.qxd 4/15/05 12:23 AM Page 119
In the world of DTDs, elements can be defined to contain four types of con- tent, as listed in Table 8-2.
Table 8-2 Types of Content Found in Elements
Content Type Example What It Means
ANY <!ELEMENT Name ANY> Allows any type of element content, either data or ele- ment markup information.
EMPTY <!ELEMENT Name EMPTY> Specifies that an element must not contain any content.
(Not as silly as it sounds.) Mixed content <!ELEMENT Name #PCDATA>
Or
<!ELEMENT Name(#PCDATA Allows an element to contain
| ChildName)*> character data or a combina- tion of subelements and char- acter data.
Element content <!ELEMENT Name(Child1, Specifies that an element can Child2)> contain only subelements, or
children.
Perhaps you’re wondering what the commas (,) and the pipe bars (|) in the table’s examples mean. Stay tuned; we discuss them in an upcoming section (“Adding mixed content”).
Using the EMPTY element type and the ANY element type
Sometimes, you may want an element type to remain empty with no content to call its own, so you use an empty element instead of an element with an opening tag and a closing tag. (Check out Chapter 4 to see the proper XML markup for empty elements.) Empty elements are like boxes you put in place but want left empty. To use them, first you have to point them out to the processor — by declaring them. Such a declaration looks like this:
<!ELEMENT Name EMPTY>
120 Part III: Building In Validation with DTDs and Schemas
14_588451 ch08.qxd 4/15/05 12:23 AM Page 120
In our example DTD, the sourceelement is an empty element:
<!ELEMENT source EMPTY>
The sourceelement does include an attribute (sourceType), but it has no content.
If (on the other hand) you want your element to serve as a catch-all box that you can put anything in, you may want to use another type of content specifi- cation: ANY. If you declare an element to contain ANYcontent, you allow that element type to hold any element or character data. Using the ANYcontent specification creates no structure to speak of, however, so it’s rarely used.
Adding mixed content
Mixed contentallows elements to contain character data, or character data and child elements. In other words, it allows elements to contain a mixture of information types. Even if an element actually contains only character data, it’s still said to contain mixed content.
Keep in mind that mixed content is one of four valid types of element con- tent. (The other three are element content [children], ANY, and EMPTY.) The basic structure for a mixed-content element declaration is as follows:
<!ELEMENT Name (#PCDATA | Child1 | Child2)*>
If the element contains only character data, then the structure looks like this:
<!ELEMENT Name #PCDATA>
White space is not recognized within parentheses in DTDs. For example, (#PCDATA)is the same as ( #PCDATA ).
In the following example, we take some liberties with our basic example DTD and fiddle with the declaration for the authorelement. The string
<!ELEMENTbegins the declaration; authoris the element name.
<!ELEMENT author (#PCDATA | publisher )*>
Including #PCDATAmeans that the element may contain parsed character data, which is text that the document processor actually looks at and interprets to display both content and markup. (That’s what the PC part is referring to — parsed character.) For example, entity references in the character data are replaced with their entity values. Whenever you want your element to con- tain parsed character data, use the #PCDATAkeyword. If you simply want an element to contain character data with no markup, use (#CDATA)by itself for the content definition part.
121
Chapter 8: Understanding and Using DTDs
14_588451 ch08.qxd 4/15/05 12:23 AM Page 121
What does |signify? In this example, |means that the authorelement may contain parsed character data ora publisherelement. The purpose of mixed content is to enable the author to specify an element that may contain both text and other elements.
With mixed content, you can’t control the order of the elements or how many times they appear. In effect, you give up control over some features of docu- ment structure when you use mixed-content models.
In the preceding example, you also find the element name publisher. This means that the element named publishermay nest within the parent ele- ment author. The *in the preceding markup is required in mixed-content element type declarations that contain both text and elements. It means that any number of the preceding group can appear — in other words, #PCDATA and/orany number of the nested elements listed. See the following section for more information on symbols in declarations.
You can work with mixed content in one of two ways:
Use only parsed character data
Allow an element to contain both text and other elements (In that case, don’t forget the asterisk!)
Using element content models
An element content modeldescribes the child elements that an element can contain. The basic structure is
<!ELEMENT Name (childName)>
which states that the element Namemust contain the childNameelement.
In the following example, as with all element declarations, !ELEMENTbegins the declaration. Then the element receives its name, books. Next comes the content specification, which states that booksmay have a child, book. The + is an occurrence indicator that states the bookelement must occur at least once — but also that it can be used as many times as needed. For clarity, the +is also called the one or more timesoccurrence indicator.
<!ELEMENT books (book+)>
The element content model uses occurrence indicators to control the order and number of times that elements can occur. Take a look at Table 8-3.
122 Part III: Building In Validation with DTDs and Schemas
14_588451 ch08.qxd 4/15/05 12:23 AM Page 122
Table 8-3 Occurrence Indicators
Symbol Example What It Means
,(comma) <!ELEMENT customer All child elements listed must be (custNumber, used in the sequence shown.
lastName, firstName, address, city, state, zip, phone, email)>
|(pipe bar) <!ELEMENT books Either the book1element or the (book1 | book2)> book2element may occur inside
books.
(No symbol) <!ELEMENT books Indicates that a single occurrence (book)> of bookmust occur inside books.
+(plus sign) <!ELEMENT books The bookchild element must be (book+)> used one or more times inside
books.
*(asterisk) <!ELEMENT books The bookelement may be used (book*)> zero or more times within books.
?(question mark) <!ELEMENT books The bookelement may be used (book?)> once or not at all within books.
Apply what you just read to our example. You use the ,(comma) occurrence indicator to imply sequence when listing elements. The example DTD uses the following content model for the customerelement:
<!ELEMENT customer (custNumber, lastName, firstName, address, city, state, zip, phone, email)>
The preceding declaration means that custNumbermust precede lastName, which must precede the firstNameelement, and so on when nested within a parent customerelement.
Declaring Attributes
In the “Using Element Declarations” section earlier in this chapter, you found out how to declare an element. In this section, you need to define an ele- ment’s better half: its attributes. In techie terms, you need to include attribute-list declarationsin your DTD whenever you want elements to use associated attributes. The attribute-list declaration lists all attributes that may be used within a given element and also defines each attribute’s type and default value.
123
Chapter 8: Understanding and Using DTDs
14_588451 ch08.qxd 4/15/05 12:23 AM Page 123