X Xerobit

XSD Schema Validation — Validate XML Against a Schema in JavaScript and Python

XSD (XML Schema Definition) defines the structure, data types, and constraints for XML documents. Learn how to write XSD schemas, validate XML in Node.js and Python, and...

Mian Ali Khalid · · 5 min read
Use the tool
XML Formatter
Format, validate, and beautify XML documents.
Open XML Formatter →

XSD validates that XML documents conform to a defined structure. Unlike JSON Schema, XSD is itself written in XML and supports rich type inheritance, namespaces, and data type constraints.

Format and inspect XML with the XML Formatter.

XSD schema basics

<!-- person.xsd — schema for person.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<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="age"  type="xs:integer"/>
        <xs:element name="email" type="xs:string" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:integer" use="required"/>
    </xs:complexType>
  </xs:element>

</xs:schema>
<!-- person.xml — valid against the schema above -->
<?xml version="1.0" encoding="UTF-8"?>
<person id="1">
  <name>Alice</name>
  <age>30</age>
  <email>alice@example.com</email>
</person>

Common XSD built-in types

<!-- String types: -->
xs:string            <!-- Any string -->
xs:normalizedString  <!-- No leading/trailing whitespace, no newlines -->
xs:token             <!-- Normalized + no consecutive whitespace -->

<!-- Numeric types: -->
xs:integer           <!-- ...,-1, 0, 1,... -->
xs:positiveInteger   <!-- 1, 2, 3,... -->
xs:decimal           <!-- 3.14, 1.00 -->
xs:float             <!-- Single-precision float -->
xs:double            <!-- Double-precision float -->

<!-- Date/time: -->
xs:date              <!-- 2024-05-12 -->
xs:time              <!-- 10:30:00 -->
xs:dateTime          <!-- 2024-05-12T10:30:00Z -->
xs:duration          <!-- P1Y2M3DT4H5M6S -->

<!-- Other: -->
xs:boolean           <!-- true, false, 1, 0 -->
xs:anyURI            <!-- http://example.com/path -->
xs:base64Binary      <!-- Base64-encoded binary data -->
xs:hexBinary         <!-- Hex-encoded binary data -->

Restrict types with facets

<xs:simpleType name="ageType">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="0"/>
    <xs:maxInclusive value="150"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="emailType">
  <xs:restriction base="xs:string">
    <xs:pattern value="[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="statusType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="active"/>
    <xs:enumeration value="inactive"/>
    <xs:enumeration value="pending"/>
  </xs:restriction>
</xs:simpleType>

Complex types and sequences

<!-- Order schema with nested elements: -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="orderId" type="xs:string"/>
        <xs:element name="customer">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name"  type="xs:string"/>
              <xs:element name="email" type="xs:string"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="items">
          <xs:complexType>
            <xs:sequence>
              <!-- minOccurs/maxOccurs for arrays: -->
              <xs:element name="item" minOccurs="1" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="sku"      type="xs:string"/>
                    <xs:element name="quantity" type="xs:positiveInteger"/>
                    <xs:element name="price"    type="xs:decimal"/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

Validate XML in Node.js

// npm install libxmljs2
import { parseXmlString } from 'libxmljs2';
import { readFileSync } from 'fs';

function validateXML(xmlPath, xsdPath) {
  const xmlContent = readFileSync(xmlPath, 'utf-8');
  const xsdContent = readFileSync(xsdPath, 'utf-8');
  
  const xmlDoc = parseXmlString(xmlContent);
  const xsdDoc = parseXmlString(xsdContent);
  
  const isValid = xmlDoc.validate(xsdDoc);
  
  if (isValid) {
    console.log('XML is valid');
  } else {
    console.log('Validation errors:');
    xmlDoc.validationErrors.forEach(err => {
      console.log(`  Line ${err.line}: ${err.message}`);
    });
  }
  
  return isValid;
}

validateXML('order.xml', 'order.xsd');

Validate XML in Python

from lxml import etree  # pip install lxml

def validate_xml(xml_path: str, xsd_path: str) -> bool:
    with open(xsd_path, 'rb') as f:
        xsd_doc = etree.parse(f)
    
    schema = etree.XMLSchema(xsd_doc)
    
    with open(xml_path, 'rb') as f:
        xml_doc = etree.parse(f)
    
    if schema.validate(xml_doc):
        print("XML is valid")
        return True
    else:
        print("Validation errors:")
        for error in schema.error_log:
            print(f"  Line {error.line}: {error.message}")
        return False

# Validate from string:
def validate_xml_string(xml_str: str, xsd_str: str) -> bool:
    xsd_doc = etree.fromstring(xsd_str.encode())
    schema = etree.XMLSchema(xsd_doc)
    xml_doc = etree.fromstring(xml_str.encode())
    return schema.validate(xml_doc)

xs:choice and xs:any

<!-- xs:choice: one of several elements -->
<xs:element name="contact">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <!-- Must have phone OR email, not both: -->
      <xs:choice>
        <xs:element name="phone" type="xs:string"/>
        <xs:element name="email" type="xs:string"/>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>
</xs:element>

<!-- xs:any: accept any XML element (like 'additionalProperties' in JSON Schema) -->
<xs:complexType name="extensible">
  <xs:sequence>
    <xs:element name="required" type="xs:string"/>
    <xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

Related posts

Related tool

XML Formatter

Format, validate, and beautify XML documents.

Written by Mian Ali Khalid. Part of the Data & Format pillar.