Om du har integrerat med ett enterprise-XML-API och fått ett kryptiskt avvisande med "element 'Quantity' är inte giltigt i det här sammanhanget" har du redan mött XSD — även om du inte kände till namnet. XML Schema Definition (XSD) är det språk som exakt definierar hur giltig XML ser ut: vilka element som krävs, vilka datatyper de håller, hur många gånger de får förekomma och vilka värden som är tillåtna. Den här guiden tar upp hur man skriver scheman, validerar mot dem och hanterar de fel man oundvikligen stöter på.

XSD definieras av W3C XML Schema-specifikationen, som publicerades första gången 2001. Rekommendationen XML Schema Part 2: Datatypes definierar det inbyggda typsystemet. Det är utförligt och lite svårtolkat att skriva för hand, men extremt precist — vilket är exakt vad du behöver när du utbyter data mellan system som inte tolererar tvetydighet.

Varför validera XML överhuvudtaget?

  • Fånga datafel tidigt. Validering vid API-gränsen innebär att ett saknat obligatoriskt fält misslyckas omedelbart med ett tydligt felmeddelande — inte 3 steg senare när din databasinsättning kastar en constraint-violation.
  • Dokumentera kontraktet. Ett XSD-schema är körbar dokumentation. Till skillnad från ett Word-dokument som blir inaktuellt är ett schema alltid korrekt eftersom systemet upprätthåller det.
  • Interoperabilitet. I B2B-integrationer — fakturering, orderhantering, sjukvård — validerar båda parter mot ett gemensamt publicerat schema. Om det validerar kan båda sidor bearbeta det.
  • Säkerhet. Validering avvisar felaktig indata innan den når affärslogiken, vilket minskar attackytan för XML-injektionsattacker.

Ett komplett XSD-schemaexempel

Låt oss bygga ett schema för en produktkatalog. Det här täcker de vanligast använda XSD-funktionerna — enkla typer, komplexa typer, sekvenser, attribut, kardinalitet och datatypsbegränsningar:

xml
<?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>

Mycket att ta in. Låt oss gå igenom nyckelbegreppen innan vi visar validering i praktiken.

Viktiga XSD-begrepp förklarade

  • xs:element. Deklarerar ett element. minOccurs och maxOccurs styr kardinalitet. Standardvärdet för båda är 1. Använd maxOccurs="unbounded" för obegränsad upprepning.
  • xs:complexType. Ett element som innehåller underordnade element eller har attribut. Enkla element (bara text) använder xs:simpleType eller en inbyggd XSD-typ direkt.
  • xs:sequence. Underordnade element måste visas i definierad ordning. Alternativet är xs:all (valfri ordning, var och en högst en gång) eller xs:choice (exakt ett av de listade elementen).
  • xs:attribute use="required". Gör attributet obligatoriskt. use="optional" (standard) tillåter att det saknas. Lägg till default="value" för ett standardvärde när det saknas.
  • xs:restriction. Begränsar en bastyp. Vanliga facetter: xs:pattern (regex), xs:enumeration (tillåtna värden), xs:minInclusive/xs:maxInclusive (numeriska gränser), xs:minLength/xs:maxLength (stränglängd).
  • xs:simpleContent + xs:extension. Sättet att lägga till attribut på ett element som också har textinnehåll — som används i PriceType ovan där <price currency="USD">149.99</price> har både text och ett attribut.

Inbyggda XSD-datatyper du faktiskt använder

  • xs:string — valfritt textinnehåll
  • xs:integer — heltal (positiva, negativa eller noll)
  • xs:nonNegativeInteger — heltal ≥ 0 (bra för kvantiteter, räknare)
  • xs:decimal — decimal med godtycklig precision (bra för priser)
  • xs:booleantrue eller false (accepterar också 1 och 0)
  • xs:dateISO 8601-datum: 2024-01-15
  • xs:dateTime — ISO 8601-datumtid: 2024-01-15T09:30:00Z
  • xs:anyURI — en URI/URL
  • xs:base64Binary — Base64-kodad binärdata

Validera XML mot XSD i Python (lxml)

Pythons standard-xml.etree.ElementTree stöder inte XSD-validering. Du behöver lxml för det. Det är värt beroendet — lxmls valideringsmeddelanden är detaljerade och pekar på exakt den rad som orsakar problemet:

bash
pip install lxml
python
from 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 > 0

Validering i Java — JAXP

Java har inbyggd XSD-validering via JAXP-validerings-API:et (inget tredjepartsbibliotek behövs). Det här är mönstret som används i enterprise Java-applikationer och Spring Boot XML-bearbetning:

java
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 vs JSON Schema

Om du har använt JSON Schema kommer XSD att kännas bekant i syfte men väldigt annorlunda i syntax. En snabb jämförelse:

  • Syntax. XSD är XML; JSON Schema är JSON. XSD är mer utförligt men integreras naturligt i XML-verktygskedjor.
  • Mognad. XSD 1.0 har funnits sedan 2001 — decennier av verktyg, validerare och biblioteksstöd. JSON Schema har utvecklats sedan 2009 och nådde ett stabilt utkast först nyligen.
  • Typsystem. XSD har ett rikare inbyggt typsystem: xs:date, xs:decimal, xs:anyURI är inbyggda. JSON Schema förlitar sig på formatannotationer för datum och URI, som validerare kan eller inte kan upprätthålla.
  • Namnrymdsstöd. XSD har inbyggt stöd för XML-namnrymder. JSON Schema har inget motsvarande koncept.
  • När man använder vilket. Om du arbetar i ett XML-ekosystem, använd XSD. Om du arbetar i ett JSON-ekosystem, använd JSON Schema. Blanda dem inte.

Vanliga valideringsfel och hur man åtgärdar dem

  • "Element X förväntas inte." Elementet visas i fel ordning i en xs:sequence, eller är inte definierat i schemat alls. Kontrollera elementnamnet och dess position relativt till syskon.
  • "Värdet Y för attributet Z är ogiltigt." Attributvärdet matchar inte sin typ eller uppräkning. Vanligt för statusfält och valutakoder med stavfel.
  • "Saknade underordnade element." Ett obligatoriskt underordnat element (minOccurs > 0) saknas. Kontrollera schemat för vilka element som är obligatoriska under föräldern.
  • "Inte ett giltigt värde av den atomära typen." En enkel typbegränsning misslyckades — mönster, intervall eller uppräkning. Kontrollera schemat för xs:restriction på den typen.
  • "Innehållsmodellen är inte deterministisk." Tvetydigt schema — valideraren kan inte avgöra vilken gren som gäller. Vanligtvis orsakas det av två alternativ med samma taggnamn i en xs:choice.

Relaterade verktyg

Arbetar du med XML-scheman? Dessa verktyg hjälper dig: XML-validerare för snabba välformatskontroller, XML-formaterare för att göra tät XML läsbar innan felsökning, XML-schemagenerator för att automatiskt generera ett XSD från ett exempel-XML-dokument, och XML till JSON när du hellre arbetar med JSON Schema.

Sammanfattning

XSD är utförligt, men det är rätt verktyg för att definiera strikta XML-kontrakt i enterprise- och B2B-sammanhang. De viktigaste mönstren att komma ihåg: använd xs:sequence med minOccurs/maxOccurs för att styra struktur, använd xs:restriction med xs:enumeration och xs:pattern för värdebegränsningar, och använd lxml i Python för validering med tydliga felmeddelanden. När du väl har ett fungerande schema blir det den enda källan till sanning som båda sidor av en integration kan referera till — vilket sparar mycket fram-och-tillbaka-felsökning.