XML Tutorial · Validation · Lesson 8 of 28
DTD: Document Type Definitions
Declaring elements, attributes and entities in a DTD, validating documents against it, and the limits of DTDs.
What a DTD declares
A DTD (Document Type Definition) is XML's original schema language, inherited from SGML. It says which elements may appear, in what order, and which attributes they take:
<!DOCTYPE note [
<!ELEMENT note (to, from, heading?, body)> <!-- sequence; ? = optional -->
<!ELEMENT to (#PCDATA)> <!-- text only -->
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA | em)*> <!-- mixed content -->
<!ELEMENT em (#PCDATA)>
<!ATTLIST note priority (low | normal | high) "normal"
id ID #REQUIRED>
<!ENTITY company "ReadyCamel Ltd">
]>
| Syntax | Meaning |
|---|---|
(a, b) |
a then b |
(a | b) |
a or b |
a? a* a+ |
optional, zero or more, one or more |
#PCDATA |
parsed text |
EMPTY / ANY |
no content / anything |
#REQUIRED #IMPLIED "default" |
attribute must appear / is optional / has a default |
ID IDREF CDATA (a|b) |
unique id, reference to an id, any text, enumeration |
Validating
from io import StringIO
from lxml import etree
dtd = etree.DTD(StringIO("""
<!ELEMENT note (to, from, heading?, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
<!ATTLIST note priority (low | normal | high) "normal">
"""))
docs = {
"valid": b"<note><to>Ada</to><from>Alan</from><body>Lunch?</body></note>",
"wrong order": b"<note><from>Alan</from><to>Ada</to><body>Lunch?</body></note>",
"bad attribute": b"<note priority='urgent'><to>Ada</to><from>Alan</from><body>Hi</body></note>",
}
for name, xml in docs.items():
doc = etree.fromstring(xml)
ok = dtd.validate(doc)
print(f"{name}: {'valid' if ok else 'invalid'}")
for err in dtd.error_log.filter_from_errors():
print(" ", err.message)
# Defaults apply when the document carries its DTD and the parser is asked to fill them in
parser = etree.XMLParser(attribute_defaults=True)
note = etree.fromstring(b'<!DOCTYPE note [<!ATTLIST note priority (low|normal|high) "normal">]><note/>', parser)
print("default filled in:", note.get("priority"))
Output
valid: valid
wrong order: invalid
Element note content does not follow the DTD, expecting (to , from , heading? , body), got (from to body)
bad attribute: invalid
Value "urgent" for attribute priority of note is not among the enumerated set
default filled in: normalLimits of DTDs
- No data types:
<price>can't be declared numeric, and dates are just text. - No namespace support, and the syntax isn't XML.
- Entities in DTDs are the root of XXE and "billion laughs" attacks (see XML security), so most systems disable DTD processing for untrusted input.
Today DTDs mostly appear in older standards (DocBook, XHTML 1.0) and to define named entities. New projects use XML Schema or RELAX NG.
Test yourself
Which DTD content model means 'one or more item elements'?
(item+)(item*)(item?)(item)
Show answer
Answer: (item+)
- is one or more, * zero or more, ? optional.