CodeMIRAI
0 XP

DTD and XML Schema

3 min read 3 runnable examples Quiz included

Well-formed and valid

  • Well-formed means a document follows XML's syntax rules: one root, closed and properly nested tags, quoted attributes.
  • Valid means it's well-formed and follows a particular structure: which elements are allowed, in what order, how many times, and what their values may be.

That structure is described in a DTD or an XML Schema. A program receiving XML can validate it first, and reject a bad document before trying to use it.

A DTD

A Document Type Definition can sit inside the document itself:

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library [
  <!ELEMENT library (book+)>
  <!ELEMENT book (title, author+, price?)>
  <!ATTLIST book id   ID    #REQUIRED
                 lang CDATA "en">
  <!ELEMENT title  (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT price  (#PCDATA)>
]>
<library>
  <book id="b1">
    <title>Clean Code</title>
    <author>Robert C. Martin</author>
    <price>32.50</price>
  </book>
  <book id="b2" lang="fr">
    <title>Le Petit Prince</title>
    <author>Antoine de Saint-Exupery</author>
  </book>
</library>
DTD syntaxMeans
(title, author+, price?)these children, in this order
xexactly one
x?zero or one
x+one or more
x*zero or more
(a | b)either a or b
#PCDATAtext
IDa value unique in the whole document
#REQUIRED, #IMPLIEDthe attribute must appear / may be left out
"en"a default value

This document is valid: each book has a title, at least one author, and an optional price.

Well-formed but invalid

Change the second book like this, and the document is still perfectly well-formed:

XML
  <book id="b2" lang="fr">
    <title>Le Petit Prince</title>
    <rating>5</rating>
  </book>

But it's no longer valid. The DTD never declared a rating element, and the book has lost its required author. A validator stops at the first problem:

Output
No declaration for element rating, line 19, column 23

XML Schema

An XML Schema (XSD) does the same job, with two big advantages: it's written in XML itself, and it has data types.

XML
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="title" type="xs:string"/>
              <xs:element name="author" type="xs:string" maxOccurs="unbounded"/>
              <xs:element name="price" type="xs:decimal" minOccurs="0"/>
              <xs:element name="published" type="xs:date" minOccurs="0"/>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID" use="required"/>
            <xs:attribute name="lang" type="xs:language" default="en"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
  • minOccurs and maxOccurs say how many times an element may appear (both default to 1).
  • type="xs:decimal" and type="xs:date" demand a number and a date in YYYY-MM-DD form. A DTD can only say "text".
  • use="required" makes an attribute compulsory.

Now validate this document against it:

XML
<library>
  <book id="b1">
    <title>Clean Code</title>
    <author>Robert C. Martin</author>
    <price>thirty</price>
    <published>01/08/2008</published>
  </book>
  <book>
    <author>Somebody</author>
  </book>
</library>

It's well-formed, as the runner confirms, but the schema finds four problems:

Output
line 5: Element 'price': 'thirty' is not a valid value of the atomic type 'xs:decimal'.
line 6: Element 'published': '01/08/2008' is not a valid value of the atomic type 'xs:date'.
line 8: Element 'book': The attribute 'id' is required but missing.
line 9: Element 'author': This element is not expected. Expected is ( title ).

Validating for real

With Python and the lxml package:

Python
from lxml import etree

schema = etree.XMLSchema(etree.parse("library.xsd"))
doc = etree.parse("library.xml")
if not schema.validate(doc):
    for error in schema.error_log:
        print(error.line, error.message)

Or on the command line, with xmllint, which comes with libxml2:

Terminal
xmllint --noout --schema library.xsd library.xml
xmllint --noout --valid library.xml          # validate against the document's DTD

DTD or XML Schema?

DTDXML Schema
written inits own syntaxXML
data typesnone: everything is textdozens: numbers, dates, booleans, patterns
namespacesnot supportedsupported
typical useolder formats, simple documentsmodern data exchange, web services, configuration
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.