XML Schema (XSD): popis a validace struktury

XML Schema (XSD) popisuje, jak má konkrétní XML dokument vypadat: jaké elementy a atributy smí obsahovat, v jakém pořadí a jakého typu jsou jejich hodnoty. Programy pak dokument podle schématu automaticky zkontrolují.

Autor: Aktualizováno

K čemu je schéma

Parser ověří, že je dokument dobře formovaný. Nepozná ale, jestli v objednávce nechybí zákazník nebo jestli je v ceně číslo. Tohle hlídá schéma. Když si dvě strany vyměňují data — e-shop a účetní systém, firma a úřad —, schéma slouží jako popis požadované struktury dat: povinných polí, jejich typů a pořadí. Úspěšná validace potvrzuje shodu s tímto schématem. Nezaručuje však přijetí dokumentu druhou stranou: aplikace může navíc kontrolovat součty, existenci zákazníka nebo oprávnění odesílatele.

XSD je doporučení W3C z roku 2001 (verze 1.0, druhé vydání 2004). Verze 1.1 z roku 2012 přidala například podmínky xs:assert, ale řada nástrojů včetně rozšířené knihovny libxml2 podporuje jen XSD 1.0. Schéma je samo XML dokumentem v jmenném prostoru http://www.w3.org/2001/XMLSchema, obvykle s prefixem xs: nebo xsd:.

Příklad: objednávka a její schéma

Mějme jednoduchou objednávku:

objednavka.xml
<?xml version="1.0" encoding="UTF-8"?>
<objednavka cislo="2026-117">
  <datum>2026-09-28</datum>
  <zakaznik>Jana Nováková</zakaznik>
  <polozka mnozstvi="2">
    <nazev>Hrnek</nazev>
    <cena mena="CZK">199.00</cena>
  </polozka>
</objednavka>

Schéma, které popisuje její strukturu:

objednavka.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="objednavka">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="datum" type="xs:date"/>
        <xs:element name="zakaznik" type="xs:string"/>
        <xs:element name="polozka" type="PolozkaTyp" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="cislo" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="PolozkaTyp">
    <xs:sequence>
      <xs:element name="nazev" type="xs:string"/>
      <xs:element name="cena" type="CenaTyp"/>
    </xs:sequence>
    <xs:attribute name="mnozstvi" type="xs:positiveInteger" default="1"/>
  </xs:complexType>

  <xs:complexType name="CenaTyp">
    <xs:simpleContent>
      <xs:extension base="xs:decimal">
        <xs:attribute name="mena" type="MenaTyp" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:simpleType name="MenaTyp">
    <xs:restriction base="xs:string">
      <xs:enumeration value="CZK"/>
      <xs:enumeration value="EUR"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Co schéma říká:

  • kořenem je objednavka s povinným atributem cislo,
  • uvnitř jsou v tomto pořadí datum (platné datum), zakaznik a jedna nebo víc položek,
  • položka má název a cenu; mnozstvi je kladné celé číslo a pokud chybí, bere se 1,
  • cena je desetinné číslo s povinnou měnou, a to pouze CZK nebo EUR.

Jednoduché a komplexní typy

Jednoduchý typ (xs:simpleType) popisuje hodnotu bez vnořených elementů a atributů — text, číslo, datum. Mají ho atributy a elementy, které obsahují jen text. Komplexní typ (xs:complexType) popisuje element s potomky nebo atributy. Element cena v příkladu je komplexní typ s jednoduchým obsahem: obsahuje jen číslo, ale má atribut.

Nejpoužívanější vestavěné typy:

TypPříklad hodnotyPoznámka
xs:stringJana Novákoválibovolný text
xs:integer-42celé číslo bez omezení rozsahu
xs:positiveInteger3celé číslo větší než 0
xs:decimal199.00desetinné číslo, oddělovač je vždy tečka
xs:booleantrue, false, 1, 0jiné hodnoty (ano/ne) neprojdou
xs:date2026-09-28formát RRRR-MM-DD
xs:dateTime2026-09-28T14:30:00+02:00datum a čas, volitelně s časovým pásmem
xs:anyURIhttps://www.xml.cz/adresa URI

Typy lze pojmenovat a použít opakovaně (PolozkaTyp), nebo je zapsat přímo uvnitř elementu jako anonymní (typ elementu objednavka).

Pořadí a počet prvků

ZápisVýznam
xs:sequencepotomci musí přijít přesně v uvedeném pořadí
xs:choicepoužije se právě jedna z možností
xs:allkaždý potomek nejvýš jednou, v libovolném pořadí
minOccurs / maxOccurskolikrát se prvek smí opakovat; výchozí je 1 a 1, "unbounded" = bez omezení
use="required"atribut je povinný; bez něj je atribut nepovinný

Omezení hodnot (facety)

Vestavěný typ lze zúžit pomocí xs:restriction. K dispozici jsou výčty hodnot (xs:enumeration), regulární výrazy (xs:pattern), rozsahy (xs:minInclusive, xs:maxExclusive…), délky (xs:length, xs:maxLength) a počty číslic.

<xs:simpleType name="PscTyp">
  <xs:restriction base="xs:string">
    <xs:pattern value="[0-9]{3} ?[0-9]{2}"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="SlevaTyp">
  <xs:restriction base="xs:decimal">
    <xs:minInclusive value="0"/>
    <xs:maxInclusive value="100"/>
    <xs:fractionDigits value="2"/>
  </xs:restriction>
</xs:simpleType>

Regulární výraz v xs:pattern vždy musí odpovídat celé hodnotě — nepíše se ^ ani $. Syntaxe se v detailech liší od regulárních výrazů v JavaScriptu nebo PHP.

Vestavěné typy shrnuje reference datových typů XSD, typy i komponenty schématu na jedné stránce tahák XML a JSON.

První schéma krok za krokem

Nejrychleji se XSD naučíš na vlastním příkladu. Napišme schéma pro seznam zaměstnanců, ve kterém hlídáme povinné údaje, délku jmen, formát rodného čísla, plat a povolené druhy úvazku.

zamestnanci.xml
<?xml version="1.0" encoding="UTF-8"?>
<zamestnanci>
  <zamestnanec osobni-cislo="1042" uvazek="castecny">
    <jmeno>Jana</jmeno>
    <prijmeni>Nováková</prijmeni>
    <rodne-cislo>905123/1234</rodne-cislo>
    <email>jana.novakova@example.cz</email>
    <plat>42500.00</plat>
  </zamestnanec>
  <zamestnanec osobni-cislo="1043">
    <jmeno>Petr</jmeno>
    <prijmeni>Svoboda</prijmeni>
    <rodne-cislo>8807151234</rodne-cislo>
    <telefon>+420 601 123 456</telefon>
    <plat>38000</plat>
  </zamestnanec>
</zamestnanci>

1. Kořenový element a opakování

Začni kořenem. zamestnanci obsahuje jeden nebo víc prvků zamestnanec — maxOccurs="unbounded" povoluje libovolný počet, minOccurs je ve výchozím stavu 1, takže prázdný seznam neprojde.

zamestnanci.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="zamestnanci">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="zamestnanec" type="Zamestnanec" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <!-- sem přijdou typy z dalších kroků -->
</xs:schema>

2. Vlastní jednoduché typy s omezeními

Pro hodnoty s pravidly vytvoř pojmenované typy. Každý vychází z vestavěného typu a xs:restriction ho zužuje: délkou, rozsahem, počtem desetinných míst, výčtem nebo regulárním výrazem.

Typy (vlož do xs:schema)
<!-- Rodné číslo: RRMMDD, u žen měsíc +50, od roku 2004 i +20/+70; lomítko nepovinné -->
<xs:simpleType name="RodneCislo">
  <xs:restriction base="xs:string">
    <xs:pattern value="\d{2}(0[1-9]|1[0-2]|2[1-9]|3[0-2]|5[1-9]|6[0-2]|7[1-9]|8[0-2])(0[1-9]|[12]\d|3[01])/?\d{3,4}"/>
  </xs:restriction>
</xs:simpleType>

<!-- Jméno: 1 až 50 znaků, vícenásobné mezery se sloučí -->
<xs:simpleType name="Jmeno">
  <xs:restriction base="xs:string">
    <xs:minLength value="1"/>
    <xs:maxLength value="50"/>
    <xs:whiteSpace value="collapse"/>
  </xs:restriction>
</xs:simpleType>

<!-- Plat: nezáporné číslo s nejvýš dvěma desetinnými místy -->
<xs:simpleType name="Plat">
  <xs:restriction base="xs:decimal">
    <xs:minInclusive value="0"/>
    <xs:fractionDigits value="2"/>
  </xs:restriction>
</xs:simpleType>

<!-- Úvazek: jen jedna z povolených hodnot -->
<xs:simpleType name="Uvazek">
  <xs:restriction base="xs:string">
    <xs:enumeration value="plny"/>
    <xs:enumeration value="castecny"/>
    <xs:enumeration value="dpp"/>
  </xs:restriction>
</xs:simpleType>

Rodné číslo a regulární výraz: vzor ověří tvar a rozumné hodnoty měsíce a dne, ale ne dělitelnost jedenácti — tu XSD 1.0 spočítat neumí. Na takovou kontrolu je potřeba xs:assert z XSD 1.1, Schematron, nebo kontrola v aplikaci. Stejně tak vzor nepozná, že 31. února neexistuje.

3. Struktura záznamu: povinné a nepovinné prvky

Komplexní typ popíše, co zaměstnanec obsahuje. xs:sequence určuje pořadí, minOccurs="0" dělá prvek nepovinným a maxOccurs="2" povoluje nejvýš dva telefony. Atribut osobni-cislo je povinný (use="required"), uvazek má výchozí hodnotu.

Komplexní typ (vlož do xs:schema)
<xs:complexType name="Zamestnanec">
  <xs:sequence>
    <xs:element name="jmeno" type="Jmeno"/>
    <xs:element name="prijmeni" type="Jmeno"/>
    <xs:element name="rodne-cislo" type="RodneCislo"/>
    <xs:element name="email" type="xs:string" minOccurs="0"/>
    <xs:element name="telefon" type="xs:string" minOccurs="0" maxOccurs="2"/>
    <xs:element name="plat" type="Plat"/>
  </xs:sequence>
  <xs:attribute name="osobni-cislo" type="xs:positiveInteger" use="required"/>
  <xs:attribute name="uvazek" type="Uvazek" default="plny"/>
</xs:complexType>

4. Validace a čtení chyb

Hotové schéma vlož spolu s dokumentem do validátoru na záložku Schéma. Když v dokumentu změníš úvazek na brigada, měsíc v rodném čísle na 13 a plat na 1500.555, validátor (libxml2) ohlásí, která omezení (facety) hodnota porušila:

Chyby validace
Element 'zamestnanec', attribute 'uvazek': [facet 'enumeration'] The value 'brigada'
  is not an element of the set {'plny', 'castecny', 'dpp'}.
Element 'rodne-cislo': [facet 'pattern'] The value '901323/1234' is not accepted by the pattern …
Element 'plat': [facet 'fractionDigits'] The value '1500.555' has more fractional digits
  than are allowed ('2').

Když nechceš začínat od nuly, nech si ve validátoru schéma vygenerovat z ukázkového dokumentu a pak ho zpřísni o omezení podle tohoto postupu.

Jak dokument se schématem propojit a validovat

Dokument může na své schéma odkázat atributem ze jmenného prostoru http://www.w3.org/2001/XMLSchema-instance. Pro dokument bez jmenného prostoru se použije xsi:noNamespaceSchemaLocation, pro dokument ve jmenném prostoru xsi:schemaLocation s dvojicí „URI prostoru a cesta ke schématu“.

<objednavka xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="objednavka.xsd"
            cislo="2026-117">
  …
</objednavka>

Odkaz je jen nápověda: validátor ho může použít, ale v praxi aplikace většinou předává schéma sama. Z příkazové řádky se dokument ověří nástrojem xmllint:

xmllint --noout --schema objednavka.xsd objednavka.xml

Pokud schéma cílí na jmenný prostor (targetNamespace), musí v něm být i elementy dokumentu. Téměř vždy se k tomu přidává elementFormDefault="qualified", aby do jmenného prostoru patřily i vnořené elementy, ne jen kořen. Více o tom v kapitole Jmenné prostory.

XSD, DTD, nebo něco jiného?

Starší DTD je jednodušší, ale nezná datové typy ani jmenné prostory. XSD je dnes standardem pro výměnu dat — používá ho například český formát elektronické faktury ISDOC i mnoho rozhraní státní správy. RELAX NG nabízí stručnější zápis (validátor ho umí ověřit stejně jako DTD) a Schematron doplňuje pravidla, která se v XSD 1.0 zapsat nedají (např. „datum splatnosti je po datu vystavení“).

Rozdělení schématu: include a import

Fragment uvnitř xs:schema
<xs:include schemaLocation="typy.xsd"/>
<xs:import namespace="urn:adresy" schemaLocation="adresy.xsd"/>

Include skládá schéma pro stejný cílový prostor, případně přebírá schéma bez targetNamespace. Import zpřístupňuje komponenty jiného prostoru, včetně prostoru bez názvu při vhodné deklaraci. schemaLocation uvádí, kde lze schéma hledat. O tom, které soubory se skutečně načtou, rozhoduje nastavení validátoru a jeho způsob vyhledávání souborů (resolver).

Názvy typů v odkazech jsou QName. Deklaruj jejich prefix a nezaměňuj jmenný prostor kontrolovaného XML dokumentu s namespace souboru schématu. Při načítání nedůvěryhodných schémat povol jen předem známé místní soubory.

Extension a restriction

Fragment schématu bez cílového prostoru
<xs:complexType name="Zaklad">
  <xs:sequence><xs:element name="jmeno" type="xs:string"/></xs:sequence>
</xs:complexType>
<xs:complexType name="Rozsireny">
  <xs:complexContent><xs:extension base="Zaklad">
    <xs:sequence><xs:element name="vek" type="xs:nonNegativeInteger"/></xs:sequence>
  </xs:extension></xs:complexContent>
</xs:complexType>

Extension zde za jméno přidá věk. Restriction omezuje povolené hodnoty nebo obsah; není to libovolná změna základního typu. Odvozený obsah musí splnit pravidla platného omezení. U jednoduchých typů jsou nejběžnější facety, například minInclusive a pattern.

Jedinečnost a vazby: key, unique, keyref

Fragment uvnitř deklarace kořenového elementu
<xs:key name="polozkaId">
  <xs:selector xpath="polozka"/>
  <xs:field xpath="@id"/>
</xs:key>
<xs:keyref name="vazba" refer="polozkaId">
  <xs:selector xpath="odkaz"/>
  <xs:field xpath="@polozka"/>
</xs:keyref>

Key vyžaduje úplný a jedinečný klíč, unique kontroluje jedinečnost přítomných úplných hodnot a keyref odkazuje na definovaný key nebo unique ve svém rozsahu. Selector a field používají omezenou syntaxi XPath, ne libovolný výraz XPath 3.1.

U instancí ve jmenném prostoru použij prefixy také v těchto cestách. XSD 1.1 nabízí další možnosti včetně assertions, které vyžadují procesor s podporou 1.1. Online XML.cz validátor není obecný XSD 1.1 procesor.

Úplný příklad key a keyref

vazby.xsd — XSD 1.0, kompletní dokument
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="katalog">
    <xs:complexType><xs:sequence>
      <xs:element name="kniha" maxOccurs="unbounded"><xs:complexType>
        <xs:attribute name="id" type="xs:string" use="required"/>
      </xs:complexType></xs:element>
      <xs:element name="vyber" minOccurs="0" maxOccurs="unbounded"><xs:complexType>
        <xs:attribute name="ref" type="xs:string" use="required"/>
      </xs:complexType></xs:element>
    </xs:sequence></xs:complexType>
    <xs:key name="knihaId"><xs:selector xpath="kniha"/><xs:field xpath="@id"/></xs:key>
    <xs:keyref name="vybranaKniha" refer="knihaId"><xs:selector xpath="vyber"/><xs:field xpath="@ref"/></xs:keyref>
  </xs:element>
</xs:schema>
vazby.xml — platná reference na existující knihu
<katalog><kniha id="k1"/><kniha id="k2"/><vyber ref="k2"/></katalog>

Spusť xmllint --noout --schema vazby.xsd vazby.xml nebo vlož oba soubory do záložky Schéma. Změna ref na k3 poruší keyref; změna druhého id na k1 poruší key. Identitní omezení mají rozsah v instanci elementu katalog. Nejsou univerzální databázový cizí klíč mezi libovolnými soubory.

Selector a field v XSD 1.0 používají omezenou XPath syntaxi, ne libovolné funkce XPath testeru. Pokud přidáš targetNamespace, potřebuješ odpovídající prefixy i v identitních cestách. Key vyžaduje přítomnou a jedinečnou hodnotu; unique dovoluje, aby vybraná hodnota chyběla. XSD 1.1 assertions jsou jiné pravidlo a vyžadují 1.1 procesor.

Spustitelný include a import: tři místní schémata

kniha.xsd — hlavní schéma
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:k="urn:knihy" xmlns:a="urn:adresy" targetNamespace="urn:knihy" elementFormDefault="qualified">
 <xs:include schemaLocation="typy.xsd"/>
 <xs:import namespace="urn:adresy" schemaLocation="adresy.xsd"/>
 <xs:element name="kniha"><xs:complexType><xs:sequence>
  <xs:element name="nazev" type="k:Nazev"/>
  <xs:element ref="a:mesto"/>
 </xs:sequence></xs:complexType></xs:element>
</xs:schema>
typy.xsd — stejný namespace: include
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:knihy">
 <xs:simpleType name="Nazev"><xs:restriction base="xs:string"><xs:minLength value="1"/></xs:restriction></xs:simpleType>
</xs:schema>
adresy.xsd — jiný namespace: import
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:adresy" elementFormDefault="qualified">
 <xs:element name="mesto" type="xs:string"/>
</xs:schema>
kniha.xml
<kniha xmlns="urn:knihy" xmlns:a="urn:adresy"><nazev>XML</nazev><a:mesto>Praha</a:mesto></kniha>
Všechny čtyři soubory v jednom adresáři
xmllint --nonet --noout --schema kniha.xsd kniha.xml

Validní dokument projde. Prázdný nazev poruší minLength. Odebrání prefixu a u města jej přesune do urn:knihy a validace selže. Chybějící adresy.xsd je chyba načtení schématu, nikoli chyba města. Relativní schemaLocation se řeší vůči zdroji schématu; pokud soubor předáváš jako text, nastav také základní adresu schématu (base URI), vůči níž se odkazy vyhodnotí, nebo vlastní způsob načítání souborů. Účinek --nonet závisí na verzi a loaderu libxml2; zde se oba soubory načítají lokálně. Řízení externích zdrojů vysvětluje kapitola příkazové řádky.