Example 3-6. A flexible yet data-oriented DTD describing people
<?xml version="1.0"?>
<!DOCTYPE person [
<!ELEMENT person (name+, profession*)>
<!ELEMENT name EMPTY>
<!ATTLIST name first CDATA #REQUIRED
last CDATA #REQUIRED>
<!-- The first and last attributes are required to be present
but they may be empty. For example,
<name first="Cher" last=""> -->
<!ELEMENT profession EMPTY>
<!ATTLIST profession value CDATA #REQUIRED>
]>
<person>
<name first="Alan" last="Turing"/>
<profession value="computer scientist"/>
<profession value="mathematician"/>
<profession value="cryptographer"/>
</person>
The DTD here is contained completely inside the internal DTD subset.
First a person ELEMENT declaration states that
each person must have one or more
name children, and zero or more
profession children, in that order. This allows
for the possibility that a person changes his name or uses aliases.
It assumes that each person has at least one name but may not have a
profession.
This declaration also requires that all name
elements precede all profession elements. Here the
DTD is less flexible than it ideally would be.
There's no particular reason that the names have to
come first. However, if we were to allow more random ordering, it
would be hard to say that there must be at least one
name. One of the weaknesses of DTDs is that it
occasionally forces extra sequence order on you when all you really
need is a constraint on the number of some element. Schemas are more
flexible in this regard.
Both name and profession
elements are empty so their declarations are very simple. The
attribute declarations are a little more complex. In all three cases
the form of the attribute is open, so all three attributes are
declared to have type CDATA. All three are also
required. However, note the use of comments to suggest a solution for
edge cases such as celebrities with no last names. Comments are an
essential tool for making sense of otherwise obfuscated DTDs.