An XML document that parses without errors is well-formed — but well-formed is not the same as correct. XSD validation goes one level deeper, checking every element against a contract: required fields are present, data types match, values fall within allowed ranges, and elements appear in the right order. This guide explains exactly what XSD validation checks, how to run it online and in code, what the most common errors mean, and how to build XSD validation into your CI pipeline so broken documents never reach production.
What is XSD validation?
XSD stands for XML Schema Definition. It is a W3C standard that lets you describe the exact structure an XML document must follow: which elements are allowed, what order they appear in, what data types their content must match, and which attributes are required or optional. When you validate an XML document against an XSD, a parser reads both files and reports every place the document deviates from the schema contract.
XSD replaced the older DTD (Document Type Definition) format as the primary XML schema language because it is itself written in XML, supports a rich set of built-in data types (`xs:integer`, `xs:date`, `xs:boolean`, etc.), and can express complex constraints like string patterns, numeric ranges, and conditional element requirements. DTDs are still encountered in legacy systems, but XSD is the standard for any XML-based data exchange built in the last fifteen years.
Where XSD validation is used
XSD validation appears wherever structured XML data crosses a system boundary. Common examples include SOAP web service requests and responses (described by WSDL, which embeds XSD schemas), e-invoicing and procurement formats like UBL 2.1 and EDIFACT, healthcare data exchange using HL7 CDA and FHIR XML profiles, government XML filings (tax returns, customs declarations), and configuration files for enterprise software like Maven's `pom.xml` or Spring's application context XML.
- SOAP / WSDL services: Every request and response element is defined in an embedded XSD.
- E-invoicing (UBL, CII): Procurement and invoice formats carry public XSD schemas that trading partners validate against.
- Healthcare (HL7, FHIR XML): Clinical document exchange requires strict XSD conformance before acceptance.
- Government filings: Tax authorities and customs agencies publish XSD schemas that submitted documents must satisfy.
- Build tooling: Maven pom.xml, Ant build.xml, and many other config formats use XSD for IDE auto-complete and validation.
Note
Well-formedness vs. validity
Every XML document must first be well-formed before it can be validated. Well-formedness is checked by the XML parser itself, with no schema needed. Validity is an additional layer checked against a specific schema. Understanding the distinction saves significant debugging time — an XSD validator that receives a malformed document will often report confusing schema errors instead of the real parse-level problem.
A textual object is a well-formed XML document if it matches the production labeled document and satisfies all the well-formedness constraints given in the specification.
What well-formedness checks
- Tag matching: Every opening tag has a corresponding closing tag (`<item>` → `</item>`).
- Proper nesting: Tags must close in reverse order — `<a><b></b></a>` is valid; `<a><b></a></b>` is not.
- Single root element: The document has exactly one top-level element.
- Quoted attributes: All attribute values are wrapped in single or double quotes.
- Escaped special characters: `<`, `>`, `&`, `"` and `'` inside text content must use entity references or CDATA sections.
- Valid element and attribute names: Names start with a letter or underscore, not a digit or hyphen.
What XSD validity adds on top
Once well-formedness passes, XSD validation layers on the schema contract. This includes checking that every element declared as required by `minOccurs="1"` is actually present, that text content in typed elements matches the declared `xs:type` (no strings in integer fields), that numeric values fall within `xs:minInclusive` and `xs:maxInclusive` bounds, that string content satisfies `xs:pattern` regex restrictions, and that child elements appear in the sequence, choice, or all group defined in the schema.
| Check | Well-formedness | XSD validity |
|---|---|---|
| Tag matching and nesting | ✓ Yes | ✓ Prerequisite |
| Single root element | ✓ Yes | ✓ Prerequisite |
| Required elements present | ✗ No | ✓ Yes — minOccurs |
| Data type correctness | ✗ No | ✓ Yes — xs:integer, xs:date, etc. |
| Numeric range constraints | ✗ No | ✓ Yes — minInclusive/maxInclusive |
| String pattern matching | ✗ No | ✓ Yes — xs:pattern |
| Element ordering | ✗ No | ✓ Yes — xs:sequence / xs:choice |
| Allowed attribute values | ✗ No | ✓ Yes — xs:enumeration |
XML Well-Formedness Checker
Check your XML document for malformed tags, invalid nesting, missing root elements, and entity errors — browser-local with line-level diagnostics.
Anatomy of an XSD schema
Before you can validate XML against an XSD, you need to understand what an XSD file contains. A schema file is itself a valid XML document, with a root element of `xs:schema` in the `http://www.w3.org/2001/XMLSchema` namespace. Everything inside the schema describes what the target XML documents must look like.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- Root element declaration -->
<xs:element name="Invoice">
<xs:complexType>
<xs:sequence>
<!-- Required string — must be present exactly once -->
<xs:element name="InvoiceNumber" type="xs:string" minOccurs="1" maxOccurs="1"/>
<!-- Required date -->
<xs:element name="IssueDate" type="xs:date" minOccurs="1" maxOccurs="1"/>
<!-- Required positive integer -->
<xs:element name="TotalAmount" type="xs:decimal" minOccurs="1" maxOccurs="1"/>
<!-- Optional — 0 to many line items -->
<xs:element name="LineItem" type="LineItemType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<!-- Required attribute -->
<xs:attribute name="currency" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<!-- Reusable complex type definition -->
<xs:complexType name="LineItemType">
<xs:sequence>
<xs:element name="Description" type="xs:string"/>
<xs:element name="Quantity" type="xs:positiveInteger"/>
<xs:element name="UnitPrice" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:schema>Key XSD concepts
- xs:element: Declares an element by name and type. `minOccurs` and `maxOccurs` control cardinality.
- xs:complexType: Defines an element that contains child elements or attributes (not just text).
- xs:simpleType: Defines a type derived from a built-in type — used to add restrictions like patterns or enumerations.
- xs:sequence: Child elements must appear in the exact order listed.
- xs:choice: Exactly one of the listed child elements must appear.
- xs:all: All listed child elements must appear, in any order, each exactly once.
- xs:attribute: Declares an attribute on a complex element. `use="required"` makes it mandatory.
- xs:restriction: Adds constraints to a base type — `xs:pattern`, `xs:minInclusive`, `xs:enumeration`, etc.
Tip
How to validate XML against XSD
There are three practical ways to validate an XML document against an XSD schema: a browser-based tool for quick checks, a command-line tool for local development and scripting, and a programmatic approach for integration into application code or CI pipelines. All three approaches report the same categories of errors — the difference is where and how you run the validation.
Check well-formedness first
Before running XSD validation, confirm the XML document is well-formed. Use the XML Well-Formedness Checker to catch structural errors — a malformed document will produce misleading XSD errors and waste debugging time. Fix all parser-level issues first, then proceed to schema validation.
Validate online with Quasar Tools XML Validator
Open the XML Validator, paste your XML document into the left panel and your XSD schema into the right panel, then run the validation. The tool processes both files entirely in your browser — no data is uploaded. Each validation error is reported with the element path, the violated constraint, and the line number in the source document.
Validate on the command line with xmllint
For local development and scripting, `xmllint` from the libxml2 package is the standard CLI choice. Run `xmllint --schema schema.xsd document.xml --noout` — the `--noout` flag suppresses the document echo so only errors are printed. A clean exit with no output means the document is valid. On macOS, install via `brew install libxml2`; on Ubuntu/Debian, `apt-get install libxml2-utils`.
Validate programmatically in Java, Python, or .NET
For application-level validation, use your language's built-in XML library. Java's `javax.xml.validation` package (Xerces), Python's `lxml.etree.XMLSchema`, and .NET's `XmlSchemaSet` with `XmlReader` all support XSD validation with a few lines of code. Integrate the validation call at your API boundary or file ingestion point to reject invalid documents before they reach business logic.
# Validate document.xml against schema.xsd using xmllint
xmllint --schema schema.xsd document.xml --noout
# Output on success:
document.xml validates
# Output on failure:
document.xml:12: element TotalAmount: Schemas validity error:
Element 'TotalAmount': 'abc' is not a valid value of the
atomic type 'xs:decimal'.XML Validator
Validate XML documents for well-formedness and schema compliance — browser-local with no upload required, line-level error reporting.
Common XSD validation errors
XSD validation errors fall into predictable categories. Understanding what each error type means lets you locate and fix the problem in the source document quickly, rather than decoding unfamiliar validator output line by line.
Type mismatch errors
Type errors occur when element or attribute content does not match the declared `xs:type`. The most common: a text string like `"N/A"` in a field declared as `xs:integer`, a date in the wrong format (e.g. `15/06/2026` instead of `2026-06-15`) in an `xs:date` field, or a decimal in a field declared as `xs:positiveInteger`. Fix by correcting the value in the source document or adjusting the type declaration in the schema if the current type is too restrictive.
Missing required elements
When `minOccurs="1"` (the default for `xs:element`) and the element is absent from the instance document, the validator reports: `Element 'X': This element is not expected. Expected is one of ( Y )` or `Element 'X' is missing`. This typically means the producer of the XML omitted a mandatory field. Check the schema's `xs:sequence` to confirm which elements are required in which position.
Unexpected or undeclared elements
If your schema does not use `xs:any` and does not set `processContents="lax"`, any element not declared in the schema will produce: `Element 'X': This element is not expected`. This is the most common error when an XML producer adds a new field without updating the schema, or when the document contains a namespace prefix that is not declared. Check the element name, the parent element context, and the namespace declarations at the top of the document.
Pattern and enumeration violations
XSD supports `xs:pattern` (a regex) and `xs:enumeration` (an allowed value list) as facets on simple types. A violation looks like: `Element 'Status': [facet 'enumeration'] The value 'ACTIVE' is not an element of the set active', 'inactive', 'pending`. Check whether the schema uses case-sensitive enumeration values and whether the instance document matches the expected casing exactly.
| Error type | Typical message fragment | How to fix |
|---|---|---|
| Type mismatch | 'abc' is not a valid xs:integer | Correct the value or loosen the type |
| Missing element | 'InvoiceNumber' is missing | Add the required element to the document |
| Unexpected element | 'Notes': This element is not expected | Remove the element or add it to the XSD |
| Enumeration failure | Value 'ACTIVE' not in set | Match casing of schema enum values |
| Pattern violation | Value fails xs:pattern restriction | Fix value to match the regex pattern |
| Sequence order | Expected is 'IssueDate' not 'Total' | Reorder elements to match xs:sequence |
| Cardinality | Element 'Item' can occur max 1 times | Remove duplicate or raise maxOccurs |
Warning
XSD validation in code and CI/CD
Manual validation works for one-off checks, but production XML workflows need automated validation at every integration point. Adding XSD validation to your application code and CI pipeline ensures invalid documents are rejected before they cause data corruption, processing failures, or compliance violations downstream.
Validating in Python with lxml
from lxml import etree
def validate_against_xsd(xml_path: str, xsd_path: str) -> list[str]:
"""Returns a list of validation error messages, empty if valid."""
with open(xsd_path, 'rb') as f:
schema_doc = etree.parse(f)
schema = etree.XMLSchema(schema_doc)
with open(xml_path, 'rb') as f:
doc = etree.parse(f)
schema.validate(doc)
return [str(e) for e in schema.error_log]
errors = validate_against_xsd('invoice.xml', 'invoice.xsd')
if errors:
for err in errors:
print(err)
else:
print('Document is valid.')Adding XSD validation to GitHub Actions
For CI/CD workflows that process or generate XML, a validation step prevents broken documents from merging. The `xmllint` command is available on GitHub Actions Ubuntu runners via `sudo apt-get install -y libxml2-utils`. Add a step that runs `xmllint --schema schema.xsd document.xml --noout` on every pull request that touches XML files. A non-zero exit code fails the check and blocks the merge.
name: Validate XML
on:
pull_request:
paths:
- '**/*.xml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install xmllint
run: sudo apt-get install -y libxml2-utils
- name: Validate XML against XSD
run: |
xmllint --schema schemas/invoice.xsd data/invoices/*.xml --nooutUsing XPath to inspect specific values before validation
Before running a full XSD pass, you can use the XPath Finder and Tester to pinpoint the value of a specific element or attribute in a large XML document. XPath queries like `//Invoice/TotalAmount/text()` retrieve a field without parsing the entire file manually. This is especially useful when diagnosing a type mismatch in a document with hundreds of elements — locate the offending value first, then apply the fix.
Validation approach comparison
| Approach | Speed | Privacy | Schema support | Best for |
|---|---|---|---|---|
| Quasar Tools XML Validator | Instant | ✓ Browser-local | Well-formed + XSD | Quick one-off checks |
| xmllint CLI | Fast | ✓ Local machine | XSD, DTD, RelaxNG | Dev scripts and CI/CD |
| lxml / Xerces / .NET | Fast | ✓ In-process | XSD (full spec) | Application code |
| Online server tools | Moderate | ✗ Data uploaded | Variable | Avoid for sensitive schemas |
XSD best practices
A well-designed XSD schema makes documents easier to validate, easier to extend, and easier to maintain across schema versions. These practices apply whether you are authoring a schema from scratch or maintaining one received from an external partner.
Use named types instead of anonymous inline types
Define complex types with `xs:complexType name="..."` rather than nesting them anonymously inside element declarations. Named types can be reused across multiple element declarations, reducing repetition and making schema changes easier — update the type definition once and all elements using it inherit the change.
Prefer xs:sequence over xs:all for strict contracts
`xs:all` allows elements to appear in any order, which seems permissive but introduces ambiguity for producers and consumers. `xs:sequence` is more explicit and matches the natural reading order of most XML formats. Use `xs:all` only when element order genuinely does not matter and you are authoring the schema for a system you control; prefer `xs:sequence` for any schema that crosses organizational boundaries.
Version your schemas with a namespace
Use a target namespace URI that includes a version indicator, such as `targetNamespace="urn:example:invoice:v2"`. This makes breaking schema changes explicit — consumers on v1 will see a namespace mismatch rather than silently validating against the wrong schema version. Keep the old schema available for backward-compatible deployments during the migration window.
- Declare a targetNamespace: Avoids element name collisions when schemas are composed together via xs:import.
- Use xs:documentation: Add human-readable descriptions inside xs:annotation blocks so schema consumers understand each field.
- Set explicit minOccurs/maxOccurs: Never rely on the default — state cardinality explicitly to communicate intent.
- Use xs:restriction for constrained strings: A postcode field should use xs:pattern, not xs:string — validation catches format errors at the boundary.
- Split large schemas: Use xs:include to split a 500-line schema into per-domain files (addresses.xsd, line-items.xsd) for easier maintenance.
Tip
Note
Key takeaways
- XSD validation checks an XML document against a schema contract — element types, required fields, value ranges, and ordering — beyond basic well-formedness.
- Always confirm well-formedness with the XML Well-Formedness Checker before running XSD validation to avoid misleading error output.
- The most common XSD errors are type mismatches, missing required elements, unexpected elements, and xs:sequence ordering violations.
- Use `xmllint --schema schema.xsd document.xml --noout` on the command line for fast local validation and CI/CD integration.
- Add an XSD validation step to your GitHub Actions workflow to block invalid XML documents from merging into the main branch.
- Design XSD schemas with named types, explicit cardinality, target namespaces, and xs:restriction facets to create schemas that are both strict and maintainable.
- Use the XPath Finder and Tester to locate specific values in large XML documents before and after validation.