Jeśli integrowałeś się z enterprisowym API XML i otrzymałeś tajemnicze odrzucenie z komunikatem „element 'Quantity' jest nieprawidłowy w tym kontekście", to już spotkałeś XSD — nawet jeśli nie znałeś tej nazwy. XML Schema Definition (XSD) to język, który precyzyjnie określa, jak powinien wyglądać poprawny XML: które elementy są wymagane, jakie typy danych przechowują, ile razy mogą wystąpić i jakie wartości są dozwolone. Ten przewodnik pokazuje, jak pisać schematy, walidować względem nich i obsługiwać błędy, które nieuchronnie napotkasz.
XSD jest zdefiniowany przez specyfikację W3C XML Schema, opublikowaną po raz pierwszy w 2001 roku. Rekomendacja XML Schema Part 2: Datatypes definiuje wbudowany system typów. Jest dość rozbudowany i trochę enigmatyczny w ręcznym pisaniu, ale niezwykle precyzyjny — a właśnie tego potrzebujesz przy wymianie danych między systemami, które nie tolerują niejednoznaczności.
Po co w ogóle walidować XML?
- Wczesne wykrywanie błędów danych. Walidacja na granicy API oznacza, że brakujące wymagane pole natychmiast zgłasza czytelny błąd — nie 3 kroki później, gdy insert do bazy danych rzuca naruszenie ograniczenia.
- Dokumentowanie kontraktu. Schemat XSD to wykonywalna dokumentacja. W przeciwieństwie do dokumentu Word, który się dezaktualizuje, schemat jest zawsze dokładny, bo system go egzekwuje.
- Interoperacyjność. W integracjach B2B — fakturowanie, zarządzanie zamówieniami, opieka zdrowotna — obie strony walidują względem wspólnego opublikowanego schematu. Jeśli waliduje, obie strony mogą go przetworzyć.
- Bezpieczeństwo. Walidacja odrzuca zniekształcone dane wejściowe zanim dotrą do logiki biznesowej, zmniejszając powierzchnię ataku dla ataków XML injection.
Kompletny przykład schematu XSD
Zbudujmy schemat dla katalogu produktów. Obejmuje to najczęściej używane funkcje XSD — typy proste, typy złożone, sekwencje, atrybuty, kardynalność i ograniczenia typów danych:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- Root element -->
<xs:element name="catalog">
<xs:complexType>
<xs:sequence>
<!-- One or more product elements -->
<xs:element name="product" type="ProductType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="version" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<!-- Product complex type -->
<xs:complexType name="ProductType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="price" type="PriceType"/>
<xs:element name="stock" type="xs:nonNegativeInteger"/>
<xs:element name="categories" type="CategoriesType"/>
</xs:sequence>
<xs:attribute name="id" type="ProductIdType" use="required"/>
<xs:attribute name="status" type="ProductStatusType" use="optional" default="active"/>
</xs:complexType>
<!-- Price with currency attribute -->
<xs:complexType name="PriceType">
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="currency" type="CurrencyCodeType" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- Categories — zero or more category strings -->
<xs:complexType name="CategoriesType">
<xs:sequence>
<xs:element name="category" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- Product ID: alphanumeric, starts with P, 4-10 chars -->
<xs:simpleType name="ProductIdType">
<xs:restriction base="xs:string">
<xs:pattern value="P[A-Z0-9]{3,9}"/>
</xs:restriction>
</xs:simpleType>
<!-- Allowed product statuses -->
<xs:simpleType name="ProductStatusType">
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="discontinued"/>
<xs:enumeration value="out_of_stock"/>
</xs:restriction>
</xs:simpleType>
<!-- ISO 4217 currency codes — a subset -->
<xs:simpleType name="CurrencyCodeType">
<xs:restriction base="xs:string">
<xs:enumeration value="USD"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="GBP"/>
<xs:enumeration value="JPY"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>Sporo do przyswojenia. Omówmy kluczowe pojęcia zanim przejdziemy do walidacji w praktyce.
Kluczowe pojęcia XSD
- xs:element. Deklaruje element.
minOccursimaxOccurskontrolują kardynalność. Domyślna wartość obu wynosi 1. UżyjmaxOccurs="unbounded"dla nieograniczonej liczby powtórzeń. - xs:complexType. Element zawierający elementy podrzędne lub posiadający atrybuty. Proste elementy (tylko tekstowe) używają
xs:simpleTypelub bezpośrednio wbudowanego typu XSD. - xs:sequence. Elementy podrzędne muszą występować w zdefiniowanej kolejności. Alternatywą jest
xs:all(dowolna kolejność, każdy co najwyżej raz) lubxs:choice(dokładnie jeden z wymienionych elementów). - xs:attribute use="required". Czyni atrybut obowiązkowym.
use="optional"(domyślnie) pozwala na jego brak. Dodajdefault="value"dla wartości domyślnej gdy atrybut jest nieobecny. - xs:restriction. Ogranicza typ bazowy. Często używane facety:
xs:pattern(regex),xs:enumeration(dozwolone wartości),xs:minInclusive/xs:maxInclusive(granice liczbowe),xs:minLength/xs:maxLength(długość łańcucha). - xs:simpleContent + xs:extension. Sposób na dodanie atrybutów do elementu zawierającego jednocześnie treść tekstową — jak w
PriceTypepowyżej, gdzie<price currency="USD">149.99</price>ma zarówno tekst jak i atrybut.
Wbudowane typy danych XSD, których faktycznie używasz
xs:string— dowolna treść tekstowaxs:integer— liczby całkowite (dodatnie, ujemne lub zero)xs:nonNegativeInteger— liczby całkowite ≥ 0 (dobre dla ilości, liczników)xs:decimal— liczba dziesiętna o dowolnej precyzji (dobra dla cen)xs:boolean—truelubfalse(akceptuje też1i0)xs:date— data w formacie ISO 8601:2024-01-15xs:dateTime— data i czas ISO 8601:2024-01-15T09:30:00Zxs:anyURI— URI/URLxs:base64Binary— dane binarne zakodowane w Base64
Walidacja XML względem XSD w Pythonie (lxml)
Standardowy moduł Pythona xml.etree.ElementTree nie obsługuje walidacji XSD.
Do tego potrzebujesz lxml.
Ta zależność jest warta zachodu — komunikaty walidacji lxml są szczegółowe i wskazują dokładnie
linię powodującą problem:
pip install lxmlfrom lxml import etree
# Load the schema
with open('catalog.xsd', 'rb') as f:
schema_doc = etree.parse(f)
schema = etree.XMLSchema(schema_doc)
# Valid XML
valid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
<product id="P0012" status="active">
<name>Mechanical Keyboard</name>
<price currency="USD">189.00</price>
<stock>42</stock>
<categories>
<category>Electronics</category>
<category>Peripherals</category>
</categories>
</product>
</catalog>"""
xml_doc = etree.fromstring(valid_xml.encode())
if schema.validate(xml_doc):
print("Valid!")
else:
for error in schema.error_log:
print(f"Error at line {error.line}: {error.message}")
# Invalid XML — missing required 'stock', bad product ID format
invalid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
<product id="BADID">
<name>Test Product</name>
<price currency="USD">10.00</price>
<categories/>
</product>
</catalog>"""
invalid_doc = etree.fromstring(invalid_xml.encode())
schema.validate(invalid_doc)
for error in schema.error_log:
print(f"Line {error.line}: {error.message}")
# Line 3: Element 'product', attribute 'id': 'BADID' is not a valid value of the atomic type 'ProductIdType'.
# Line 7: Element 'categories': Missing child element(s). Expected is ( category ). ← if minOccurs > 0Walidacja w Javie — JAXP
Java ma wbudowaną walidację XSD poprzez API walidacji JAXP (nie wymaga bibliotek zewnętrznych). To wzorzec stosowany w enterprisowych aplikacjach Java i przetwarzaniu XML w Spring Boot:
import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import org.xml.sax.SAXException;
import java.io.IOException;
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("catalog.xsd"));
Validator validator = schema.newValidator();
try {
validator.validate(new StreamSource(new File("catalog.xml")));
System.out.println("Valid!");
} catch (SAXException e) {
System.out.println("Validation error: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO error: " + e.getMessage());
}XML Schema a JSON Schema
Jeśli używałeś JSON Schema, XSD będzie podobny w celu, ale bardzo różny pod względem składni. Szybkie porównanie:
- Składnia. XSD to XML; JSON Schema to JSON. XSD jest bardziej rozbudowany, ale naturalnie integruje się z narzędziami XML.
- Dojrzałość. XSD 1.0 istnieje od 2001 roku — dekady narzędzi, walidatorów i wsparcia bibliotek. JSON Schema ewoluuje od 2009 roku i dopiero niedawno osiągnął stabilny draft.
- System typów. XSD ma bogatszy wbudowany system typów:
xs:date,xs:decimal,xs:anyURIsą wbudowane. JSON Schema polega na adnotacjach formatów dla dat i URI, które walidatory mogą lub nie egzekwować. - Obsługa przestrzeni nazw. XSD ma natywną obsługę przestrzeni nazw XML. JSON Schema nie ma odpowiednika.
- Kiedy używać którego. Jeśli pracujesz w ekosystemie XML, używaj XSD. Jeśli w ekosystemie JSON, używaj JSON Schema. Nie mieszaj ich.
Najczęstsze błędy walidacji i jak je naprawić
- „Element X jest nieoczekiwany." Element pojawia się w złej kolejności w
xs:sequencelub w ogóle nie jest zdefiniowany w schemacie. Sprawdź nazwę elementu i jego pozycję względem rodzeństwa. - „Wartość Y atrybutu Z jest nieprawidłowa." Wartość atrybutu nie pasuje do jego typu lub wyliczenia. Częste w polach statusu i kodach walut z literówkami.
- „Brakujące elementy podrzędne." Wymagany element podrzędny (minOccurs > 0) jest nieobecny. Sprawdź schemat, które elementy są obowiązkowe pod rodzicem.
- „Nie jest prawidłową wartością typu atomowego." Ograniczenie typu prostego nie zostało spełnione — wzorzec, zakres lub wyliczenie. Sprawdź w schemacie
xs:restrictiondla tego typu. - „Model zawartości nie jest deterministyczny." Niejednoznaczny schemat — walidator nie może określić, która gałąź ma zastosowanie. Zwykle spowodowane przez dwie opcje z tą samą nazwą tagu w
xs:choice.
Powiązane narzędzia
Pracujesz ze schematami XML? Te narzędzia będą pomocne: Walidator XML do szybkich kontroli poprawności, Formater XML, by zagęszczony XML był czytelny przed debugowaniem, Generator schematu XML do automatycznego generowania XSD z przykładowego dokumentu XML, oraz XML do JSON, gdy wolisz pracować z JSON Schema.
Podsumowanie
XSD jest rozbudowany, ale to właściwe narzędzie do definiowania ścisłych kontraktów XML
w kontekstach enterprisowych i B2B. Kluczowe wzorce do zapamiętania: używaj xs:sequence
z minOccurs/maxOccurs do kontroli struktury, używaj xs:restriction
z xs:enumeration i xs:pattern dla ograniczeń wartości, oraz używaj
lxml w Pythonie do walidacji z czytelnymi komunikatami błędów. Gdy masz działający
schemat, staje się on jedynym źródłem prawdy, do którego mogą odwoływać się obie strony integracji —
co oszczędza mnóstwo wzajemnego debugowania.