1. Why XML validation is needed
XML is a very flexible format, sometimes too flexible. Unlike strongly typed data, XML "tolerates" mistakes, and if you forget to close a tag, or someone writes the wrong attribute name, the program might throw an error while parsing the file... or not notice the problem at all, which is worse.
Validation by XSD (XML Schema Definition) is a way to ensure that your XML file fully matches some predefined contract: what tags are called, in what order they appear, what types they have, whether they're required and how many can be present. It's like passport control for your data — the schema says: "You won't get in without the required field or if the field isn't the right length!"
Validation is important in enterprise systems, for data exchange between different systems, APIs — anywhere you don't want "silent" acceptance of malformed structures.
XSD (XML Schema Definition) is a separate XML document that formalizes the structure of another XML. In an XSD you describe: which tags are allowed, which are required, what the types of attributes or nested elements are, patterns for strings, constraints for numbers, even possible values (via restrictions and enumerations).
| XML | XSD (structure description) |
|---|---|
|
|
|
|
XSD is similar in purpose to JSON Schema in the JSON world.
2. Example XSD and corresponding XML
Example XML
<person age="23">
<name>Ivan</name>
<email>ivan@example.com</email>
</person>
Example XSD
<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="email" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="age" type="xs:positiveInteger" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
What this schema describes:
- The root element is <person>.
- Inside <person> there should be <name> (required) and <email> (optional) elements.
- <person> must have an attribute age with a positive integer value.
3. Validating XML with XSD in .NET
In .NET the classic approach is to use the namespaces System.Xml and System.Xml.Schema.
Workflow:
- Load the XML file
- Load the XSD schema
- "Attach" the schema for XML validation
- Run validation and handle possible errors
Here's a basic example:
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
// 1. Validation error handler
void ValidationCallBack(object? sender, ValidationEventArgs e)
{
Console.WriteLine($"Validation error: {e.Message}");
}
string xmlPath = "person.xml";
string xsdPath = "person.xsd";
// 2. Load schema
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", xsdPath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas = schemas;
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationCallBack;
// 3. Open XML with settings
using XmlReader reader = XmlReader.Create(xmlPath, settings);
while (reader.Read())
{
// Just traverse the document - validation happens "on the fly"
}
Console.WriteLine("Validation finished!");
Notes:
- All validation errors will be caught in the callback ValidationCallBack.
- If there are no errors — the document matches the schema (ValidationType.Schema).
- If there are mismatches (missing age, invalid email, extra field, etc.) — they'll show up in the console.
Different error types and feedback
XSD validation can "complain" for various reasons. Here are the most common scenarios and example errors:
- Missing required element (minOccurs="1" by default)
- An extra tag not described in the XSD
- Attribute of the wrong type (age="abc" instead of a number)
- Strings not matching a pattern (described via <xs:pattern>)
Typical error:
Validation error: The element 'age' with text content 'abc' is invalid. The expected type is 'xs:positiveInteger'.
4. More advanced XSDs: types, enumerations, patterns
XSD is practically a mini language for describing allowed data. Here are some advanced examples:
Enumeration of allowed values
<xs:element name="gender">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="male"/>
<xs:enumeration value="female"/>
<xs:enumeration value="diverse"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
String length restriction
<xs:element name="code">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
Email pattern (simplified)
<xs:element name="email">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="\w+@\w+\.\w+"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
You can create your own types, inherit them, use nested elements — almost like OOP!
5. Validating a complex document
Let's say our learning app now exports a list of students to XML. Time to add some code!
Example XML
<students>
<student id="1">
<name>Elena</name>
<grade>5</grade>
</student>
<student id="2">
<name>Alexey</name>
<grade>4</grade>
</student>
</students>
XSD schema
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="students">
<xs:complexType>
<xs:sequence>
<xs:element name="student" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="grade">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Validation in code (same techniques):
// Paths to files
string xmlPath = "students.xml";
string xsdPath = "students.xsd";
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", xsdPath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas = schemas;
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationCallBack;
using XmlReader reader = XmlReader.Create(xmlPath, settings);
while (reader.Read()) { /* same as before */ }
6. Useful nuances
How to reference the schema directly in XML (xsi:schemaLocation)
In practice you often see XML files where the root element has attributes xsi:schemaLocation or xsi:noNamespaceSchemaLocation:
<students xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="students.xsd">
...
</students>
This doesn't "automatically" validate the file, but helps some validators and editors find the schema for checking. In .NET validation it's not required, but it's formally correct.
On-the-fly validation and in-memory validation — with XDocument
If you work with LINQ to XML (XDocument), you can also validate an XML document in memory without using files:
using System.Xml.Linq;
using System.Xml.Schema;
// Load schema
XmlSchemaSet schema = new XmlSchemaSet();
schema.Add("", "students.xsd");
// Load XML into memory
XDocument doc = XDocument.Load("students.xml");
// Validation
doc.Validate(schema,
(o, e) => Console.WriteLine($"Error: {e.Message}"),
true // Check warnings (often noisy)
);
This approach is handy if you programmatically create or modify XML and want to make sure you haven't produced another XML monster that breaks the schema.
7. Common mistakes when validating XML with XSD
Error #1: you forgot to attach the schema or provided the wrong path to the XSD.
As a result no validation is performed, and the XML file gets through the "guards" even if it has errors. Often the path is relative and the app looks for the file in an unexpected place. For tests it's better to use an absolute path or place the schema next to the executable.
Error #2: namespaces don't match.
If your schema uses a namespace, you must pass it as the first argument when adding the schema to XmlSchemaSet.Add. Otherwise validation will fail even if the XML structure is correct. If there's no namespace — pass an empty string.
Error #3: element order mismatch.
If the schema explicitly defines a <xs:sequence>, element order matters. XML where correct elements appear in the wrong order will be considered invalid.
Error #4: multiple namespaces declared in XML.
If the document uses more than one namespace, this can lead to unexpected validation failures. Supporting multiple namespaces requires careful attention and precise schema setup.
Error #5: not collecting and storing errors.
Printing errors to the console might be fine for debugging but not for production. It's better to collect errors in a list and present them to the user in a convenient form — especially when doing bulk checks (for example, on a web server).
GO TO FULL VERSION