XML Tutorial · Validation · Lesson 9 of 28

XML Schema (XSD) Basics

Writing an XML Schema with elements, simple and complex types, attributes and occurrence rules, and validating against it.

Create a free account to save this page, take notes and track your progress. Sign in

What XSD adds

W3C XML Schema (XSD, 2001) is written in XML and adds what DTDs lack: data types (xs:integer, xs:decimal, xs:date, xs:boolean…), namespace support, and reusable types. It's the most widely used schema language in enterprise integration (SOAP/WSDL, ISO 20022, UBL, HL7).

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="customer" type="xs:string"/>
        <xs:element name="item" maxOccurs="unbounded">
          <xs:complexType>
            <xs:simpleContent>
              <xs:extension base="xs:string">
                <xs:attribute name="qty" type="xs:positiveInteger" use="required"/>
                <xs:attribute name="price" type="xs:decimal" use="required"/>
              </xs:extension>
            </xs:simpleContent>
          </xs:complexType>
        </xs:element>
        <xs:element name="delivery" type="xs:date" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:integer" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>
  • Simple types hold text (xs:string, xs:date); complex types have child elements or attributes.
  • xs:sequence (in order), xs:choice (one of), xs:all (any order, each at most once).
  • minOccurs / maxOccurs control repetition (both default to 1; unbounded means no limit).
  • Attributes are optional unless use="required".

Validating in three languages

Each language checks the same three documents against the schema above. Error messages differ between libraries, so the shared output just says valid or invalid:

Validate with an XSDTry it yourself
from lxml import etree

XSD = b"""<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="customer" type="xs:string"/>
        <xs:element name="item" maxOccurs="unbounded">
          <xs:complexType><xs:simpleContent><xs:extension base="xs:string">
            <xs:attribute name="qty" type="xs:positiveInteger" use="required"/>
            <xs:attribute name="price" type="xs:decimal" use="required"/>
          </xs:extension></xs:simpleContent></xs:complexType>
        </xs:element>
        <xs:element name="delivery" type="xs:date" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:integer" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>"""

DOCS = {
    "good": b'<order id="1"><customer>Ada</customer><item qty="2" price="3.50">Tea</item><delivery>2026-11-02</delivery></order>',
    "bad qty": b'<order id="2"><customer>Ada</customer><item qty="0" price="3.50">Tea</item></order>',
    "bad date": b'<order id="3"><customer>Ada</customer><item qty="1" price="3.50">Tea</item><delivery>02/11/2026</delivery></order>',
}
schema = etree.XMLSchema(etree.fromstring(XSD))
for name, xml in DOCS.items():
    print(name, "->", "valid" if schema.validate(etree.fromstring(xml)) else "invalid")

Output

good -> valid
bad qty -> invalid
bad date -> invalid

Reading the errors

Good error messages point at the line and the rule that failed; log them when you reject a message:

Schema error messagesTry it yourself
from lxml import etree

schema = etree.XMLSchema(etree.fromstring(b"""<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="born" type="xs:date"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:integer" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>"""))

doc = etree.fromstring(b"<person><born>1815-12-10</born><nickname>Ada</nickname></person>")
print(schema.validate(doc))
for err in schema.error_log:
    print(f"line {err.line}: {err.message}")

Output

False
line 1: Element 'person': The attribute 'id' is required but missing.
line 1: Element 'born': This element is not expected. Expected is ( name ).

Test yourself

Which XSD attribute allows an element to repeat any number of times?

  1. maxOccurs="unbounded"
  2. minOccurs="0"
  3. use="required"
  4. type="xs:list"
Show answer

Answer: maxOccurs="unbounded"

maxOccurs sets the upper limit; unbounded removes it.

Was this page helpful?