Lobster Experten Fragen

XML

XML steht für EXtensible Markup Language, also eine erweiterbare Auszeichnungs-Sprache. Es ist simpel, gut menschenlesbar und sehr mächtig. Und: Es braucht unglaublich viel Platz. Da allerdings sehr viele Textteile sehr oft wiederholt werden, lässt es sich auch wunderbar komprimieren, was den Nachteil wieder einigermaßen wett macht. Eine ausführliche Darstellung findet sich bei Wikipedia, hier konzentrieren wir uns auf das, was für EDI/EAI wichtig ist.

Ein einfaches Beispiel


    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Auftrag>
  <Kopf>
    <AuftragsNr>4711</AuftragsNr>
    <KundenNr>K0815</KundenNr>
    <Auftragsdatum>2013-05-30T00:00:00+2</Auftragsdatum>
  </Kopf>
  <Positionen>
    <Position nr="1">
      <ArtNr>S123</ArtNr>
      <Menge>5</Menge>
      <Einzelpreis>9.99</Einzelpreis>
    </Position>
    <Position nr="2">
      <ArtNr>H456</ArtNr>
      <Menge>3</Menge>
      <Einzelpreis>17.95</Einzelpreis>
    </Position>
  </Positionen>
</Auftrag>

Der Aufbau

Zuallererst muss gesagt werden, dass die Zeilenumbrüche und Einrückungen in obigem Beispiel der besseren Lesbarkeit dienen. Oft werden Sie Dokumente so formatiert vorfinden, aber im Prinzip kann auch alles in einer Zeile direkt hintereinander stehen. Das ist für die Bedeutung völlig egal. Whitespaces (Zeilenumbruch, Leerzeichen, TAB) außerhalb von Werten haben keinerlei Wirkung. Sollten in bestimmten Werten (beispielsweise Artikelbeschreibungen) Zeilenumbrüche vorkommen, werden die natürlich übernommen. Und nun zu den Bestandteilen.

XML-Header

Eingeleitet wird die XML-Datei vom XML-Header. Der nennt die Version des XML-Formats (eigentlich immer 1.0) und das Encoding, in dem die Datei vorliegt. Außerdem ist hier mit standalone=“yes“ angegeben, dass diese Datei nicht zwingend gegen eine Formatdefinition geprüft werden muss. Mehr dazu etwas später. Dieser Header sollte eigentlich immer existieren, es gibt aber reichlich Beispiele für XML-Dateien, die ohne daherkommen.
Allgemein nennen sich Angaben, die in <? und ?> geklammert sind, „Processing Instructions“. Dieser Header sieht auch so aus, ist aber technisch eigentlich keine. Processing Instructions haben keine direkte Bedeutung für den Daten-Inhalt, daher beschäftigen wir uns nicht näher damit.

Tags und Attribute

Die Bezeichner zwischen < und > nennt man Tags (gesprochen: Täg). Jedes Tag wird, so es nicht ohne Inhalt in der Gegend herumsteht, geöffnet und wieder geschlossen. Man spricht auch vom öffnenden und vom schließenden Tag. Das schließende Tag enthält einfach noch einen Slash / vor dem Bezeichner.

    • Auf oberster Ebene, also ohne selbst „Kind“ eines „Vater-Tags“ zu sein, darf es nur exakt einen Tag pro Datei geben: Das sogenannte Root-Tag. Eine XML-Datei, die mehr als ein (öffnendes und sein schließendes) Tag auf oberster Ebene hat, ist ungültig.
    • Ein Tag kann einen einfachen Wert beinhalten, wie hier: <AuftragsNr>4711</AuftragsNr>
    • Ebenso möglich ist, dass ein Tag weitere Tags beinhaltet, und das beliebig tief geschachtelt. Das obige Beispiel zeigt das sehr schön.
    • Ein Tag selbst kann durchaus mehrfach auftreten. So könnte man z.B. die Einordnung eines Artikels in eine Katalogstruktur so darstellen:

<Artikel nr="4711" name="Lernspiel abc">
  <Warengruppe>Lernen</Warengruppe>
  <Warengruppe>Spiele</Warengruppe>
  <Warengruppe>Schule</Warengruppe>
  <Warengruppe>Kinder</Warengruppe>
</Artikel>

Immerhin passt der Artikel in alle vier Gruppen thematisch gut rein.
Und der Auftrag in unserem Anfangsbeispiel hat ja auch mehrere Positionen.

    • Neben Werten und anderen Tags kann so ein Tag auch noch beliebig viele Attribute haben, wie dieses hier: <Position nr=“2″> … </Position>
      Der Wert eines Attributs wird – anders als im alten HTML – immer in „“ gesetzt.
      Attribute stehen grundsätzlich im öffnenden Tag. Wichtig ist, dass ein Attribut in einem Tag nur einmal auftauchen kann.
      So was geht also nicht: <Position nr=“2″ nr=“3″> Wäre auch irgendwie seltsam, welche Nummer hat die Position denn nun?
    • Und, besonders schön: Ein Tag kann alles auf einmal beinhalten:

<Obertag attr1="bla" attr2="fasel" attr3="dingens">
  <Untertag1 noch_n_attr="Vroni">
    <UnterUntertag_a>Halli</UnterUntertag_a>
    <UnterUntertag_b>Hallo</UnterUntertag_b>
    <UnterUntertag_c>Hallöle</UnterUntertag_c>
     Das ist der Wert vom Untertag 1.
  </Untertag1>
  <Untertag2>Ja servus!</Untertag2>
  Das ist der Wert vom Obertag.
</Obertag>
  • Sollte ein Tag mal weder Wert noch Untertags beinhalten, kann es auch verkürzt geschrieben werden. Man zieht öffnendes und schließendes Tag zusammen:
    <Tag_ohne_Inhalt attr1=“Attribute dürfen sein“ />
    Sie sehen den Slash / am Ende? Der sagt, das Tag ist hier gleich wieder zu Ende. Es folgt kein schließendes Tag mehr.
  • Kommentare werden mit <!– eingeleitet und mit –> wieder geschlossen. Sie dürfen über mehrere Zeilen gehen, nur zwischen einzelnen Tags stehen und niemals geschachtelt werden.

Und damit wäre eigentlich der Aufbau von xml auch schon ausreichend beschrieben. So weit, so einfach. Aber ein Bisschen kommt da noch.

Sonderzeichen und Schreibweisen

Was passiert, wenn ein < oder ein > in einem Wert vorkommen? Das würde jeden XML-Parser aus dem Tritt bringen. Der sieht plötzlich ein < und denkt, da geht ein neues Tag auf. Dabei steht da eigentlich nur „a < b“. Damit der Parser nicht verzweifelt, maskiert man solche Zeichen, und zwar mit sogenannten Entitäten (engl.: entities). Aus einem < wird &lt;. Jede Entität beginnt mit einem Ampersand (alias Kaufmanns-Und) & und endet mit einem Strichpunkt ;. Dazwischen steht der Code für das Zeichen. In &lt; steht das „lt“ für „lower than“, also „kleiner als“. Entsprechend wird das > mit &gt; codiert, „gt“ für „greater than“, also „größer als“.
Und da hiermit das Ampersand & auch seine Spezialbedeutung bekommen hat, muss auch dieses codiert werden: &amp;. Sie erraten es sicher: „amp“ für „Ampersand“.
Der Strichpunkt ; muss nicht codiert werden. Da er ja nur dann gilt, wenn vorher ein & mit passendem Code steht, verwirrt ein alleinstehender Strichpunkt den Parser nicht.
Was aber gar nicht gut ist, sind Anführungszeichen “ in Attributwerten. Zumindest da müssen auch sie codiert werden und zwar mit &quot; („quot“ für „quotation mark“). Und, zu guter letzt, auch das einfache Anführungszeichen alias Apostroph ‚ muss maskiert werden: &apos;
Übrigens darf man auch die Zahlenwerte der ASCII- bzw. Unicode-Codes der maskierten Zeichen zwischen & und ; schreiben. Aber die sind nicht so gut zu merken wie lt, gt, amp, quot und apos.
Wer noch die Erfahrung gemacht hat, HTML-Seiten im Texteditor zu erstellen (vi rules!), wird nun sofort an die vielen weiteren Entitäten denken, die er dort benutzt hat. Zum Beispiel &Auml; für das große Ä oder &szlig; für das scharfe ß. Das kommt noch aus einer Zeit, als das Netz praktisch nur ASCII kannte. Da musste alles, was nicht im ASCII-Code vorkam, so codiert werden. Inzwischen schreibt man auch in HTML-Seiten oben das Encoding rein (wenn man sauber arbeitet), und die Browser kommen heute auch eigentlich alle mit Latin1 und Unicode klar, sodass das Codieren von Umlauten & Co. entfällt.

Was die Schreibweisen angeht, gilt zuallererst: XML ist case sensitive, will heißen, die Groß-/Kleinschreibung ist wichtig. Also ist DAS nicht gleich das nicht gleich Das. Bitte beachten Sie das unbedingt: Auf manchen Systemen wird man ja zur Schludrigkeit erzogen.
Außerdem gibt es noch ein paar Regeln für Bezeichner, also die Namen von Tags und Attributen. Leerzeichen sind auf jeden Fall tabu. Sie trennen immerhin Tagnamen und Attribute in einem Tag. Auch Entitäten dürfen nicht enthalten sein. Bei Sonderzeichen wie Umlauten muckt auch der eine oder andere Parser.
Kurz, nur folgende Zeichen sind erlaubt: Kleine oder große Buchstaben (a-z|A-Z), Ziffern, Unterstrich _, Bindestrich – und der Punkt. Doppelpunkte haben eine Sonderbedeutung und werden weiter unten noch behandelt. Außerdem darf kein Bezeichner mit „xml“ (egal ob groß, klein oder gemischt geschrieben) beginnen. Das ist für spezielle Zwecke reserviert.

Regeln für die Praxis

Wir sprechen hier im folgenden explizit von Daten-XML-Strukturen. Da ist einiges einfacher. Es gibt auch noch Dokumenten-Strukturen. Zum Beispiel werden Dokumente von Open Office, Neo Office, KOffice usw. ebenfalls in einem XML-Format gespeichert. Eine Datei des Open Document Formats ist letztlich nichts anderes als ein ZIP-Archiv mit einer Handvoll XML-Dateien (und evtl. eingebetteten Bildern etc). Eine davon – und zwar normalerweise die weitaus größte – enthält z.B. den Text aus OO-Writer. Hier gelten ganz besondere Regeln, beispielsweise ist die Reihenfolge aller Elemente innerhalb einer Datei enorm wichtig. Wenn Sie sich noch mal die obigen Beispiele ansehen, egal ob den Auftrag oder Artikel mit seinen Warengruppen, ist schnell erkennbar, dass hier die Reihenfolge herzlich egal ist. Die Positionen enthalten das Attribut nr und die Warengruppen des Artikels können auch beliebig umgestellt werden. Na gut, das „Halli Hallo Hallöle“ im letzten Beispiel verliert irgendwie seinen Witz, wenn man’s umstellt, …
Die folgenden Regeln gelten also für XML-Dateien, die reine Daten ohne Dokumentcharakter transportieren (wenn auch einige in beiden Fällen angewandt werden können):

  • Wenn weder Wert noch Untertags gegeben sind, ist die Kurzform immer äquivalent zur langen Schreibweise aus öffnendem und schließendem Tag:
    <Tag attr=“abc“></Tag> ist gleichwertig mit <Tag attr=“abc“ />
  • Ein vollständig leeres Tag (also auch ohne Attributwerte) kann auch ganz weggelassen werden: <Leer /> kann entfallen.
  • Hat ein Attribt keinen Wert, kann es ebenso weggelassen werden: <Tag leer=““ >wert</Tag> ist gleichwertig mit <Tag>wert</Tag>
  • Die Reihenfolge von Tags oder Attributen ist irrelevant. Jegliche Ordnungskriterien werden durch die Daten, wie z.B. eine Positionsnummer im Anfangsbeispiel, dargestellt.
    
    <Obertag attr1="bla" attr2="fasel" attr3="dingens">
      <Untertag1>Text1</Untertag1>
      <Untertag2 attr1="a" attr2="b" attr3="c">Text2</Untertag2>
      <Untertag3>Text3</Untertag3>
    </Obertag>
    

    ist äquivalent zu:

    
    <Obertag attr1="bla" attr3="dingens" attr2="fasel">
      <Untertag2 attr3="c" attr2="b" attr1="a">Text2</Untertag2>
      <Untertag3>Text3</Untertag3>
      <Untertag1>Text1</Untertag1>
    </Obertag>
    

Leider muss nun noch gesagt, werden, dass vor allem viele „selbstgestrickte“ XML-Parser das anders sehen. Da wird dann schon mal ein leeres Tag zwingend erwartet, und zwar nur in einer ganz bestimmten Form (<lang></lang> oder <kurz />). In manchen Datenformaten hat auch die Reihenfolge der Tags eine Bedeutung, statt auf saubere Attribute wie z.B. oben bei den Positionsnummern zu setzen.

Schemata

Ein weiterer Vorteil von XML-Strukturen ist, dass man ihren Aufbau in einer allgemein gültigen Form genau definieren kann. Während es zu Formaten wie EDIfact oder VDA mehr oder minder gut menschenlesbare Beschreibungen plus einen Satz allgemeiner Regeln gibt, kann der Aufbau einer XML-Struktur komplett maschinenlesbar dargestellt werden. Dies geschah früher durch eine sogenannte DTD (= Document Type Definition), heute ist der Standard das XML Schema XSD (= XML Schema Definition). Es gibt noch andere Ansätze, aber XSD ist heute am weitesten verbreitet. So ein Schema ist selbst wiederum in XML verfasst, was ein großer Vorteil gegenüber der gewöhnungsbedürftigen Notation der alten DTDs ist. Außerdem ist es mächtiger als diese.

Ein Schema für unser Eingangsbeispiel könnte so aussehen:


<?xml version="1.0" encoding="UTF-8"?>
<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <s:element name="Kopf" minOccurs="1" maxOccurs="1">
    <s:complexType>
      <s:sequence>
        <s:element name="AuftragsNr" type="s:string" minOccurs="1" maxOccurs="1"/>
        <s:element name="KundenNr" type="s:string" minOccurs="1" maxOccurs="1"/>
        <s:element name="Auftragsdatum" type="s: time" minOccurs="1" maxOccurs="1"/>
      </s:sequence>
    </s:complexType>
  </s:element>
  <s:element name="Position" minOccurs="1" maxOccurs="unbounded">
    <s:complexType>
      <s:sequence>
        <s:element name="ArtNr" type="s:string" minOccurs="1" maxOccurs="1"/>
        <s:element name="Menge" type="s:integer" minOccurs="1" maxOccurs="1"/>
        <s:element name="Einzelpreis" type="s:float" minOccurs="1" maxOccurs="1"/>
        <s:element name="Bezeichnung" type="s:string" minOccurs="0" maxOccurs="1"/>
      </s:sequence>
      <s:attribute name="nr" type="s:integer"/>
    </s:complexType>
  </s:element>
  <s:element name="Positionen" minOccurs="1" maxOccurs="1">
    <s:complexType>
      <s:sequence>
        <s:element ref="Position" minOccurs="1" maxOccurs="unbounded"/>
      </s:sequence>
    </s:complexType>
  </s:element>
  <s:element name="Auftrag" minOccurs="1" maxOccurs="1">
    <s:complexType>
      <s:sequence>
        <s:element ref="Kopf" minOccurs="1" maxOccurs="1"/>
        <s:element ref="Positionen" minOccurs="1" maxOccurs="1"/>
      </s:sequence>
    </s:complexType>
  </s:element>
</s:schema>

Das ist weiters erst mal kein Hexenwerk. Wenn Sie das Schema mit den Beispieldaten vergleichen, werden Sie schnell hinter die wichtigsten Prinzipien kommen.
Sinn und Zweck solcher Schemata ist zum einen, den maximal möglichen Aufbau einer XML-Datei vorzugeben. Maximal, denn das Element „Bezeichnung“ kommt in unserem Beispiel ja nicht mal vor. Das Schema beschreibt also unter Umständen mehr, als in den realen Daten steht. Zum anderen dient das Schema dazu, vorliegende XML-Daten auf ihre Validität zu prüfen. Man nimmt also so eine Auftrags-Datei und vergleicht sie mit dem Schema, ob womöglich irgend ein Element oder Attribut vorkommt, das laut Schema nicht erlaubt ist, ob etwas fehlt, das Pflicht wäre (minOccurs=“1″) oder umgekehrt öfter vorkommt als es dürfte. Auch kann man in einem Schema eine Aufzählung von Werten angeben, die ein Attribut oder Tag überhaupt haben darf, und natürlich den Typ bestimmter Werte. Man kann – ähnlich wie in Programmiersprachen – eigene Typen definieren, die von anderen abgeleitet sind, und noch eine Menge andere Dinge.
Da eine tiefergehende Behandlung von XSD hier zu weit führen würde, nur noch eines: Komplexe XML-Strukturen werden gern in mehreren Schema-Dateien definiert. Es gibt eine zentrale XSD, die via include- oder import-Anweisungen weitere Schemata einbindet. So kann man in einer Datei bestimmte Grundtypen z.B. für Preisangaben, Telefonnummern oder Web-Adressen definieren, in einer anderen alles, was mit Kundeninformationen zu tun hat, in der nächsten alles artikelbezogene, und dann nutzt man das alles im zentralen Dokument, das den Aufbau eines Auftrags oder Katalogs beschreibt.

Es wäre so schön einfach gewesen – Namensräume

Tja, so weit wäre XML eine wunderbar einfache Sache. Und so lesbar. Aber leider gibt es da etwas, das die Sache komplizierter macht: Namensräume, englisch Namespaces. Wie gerade schon angesprochen, gibt es komplexe Strukturen, die auch in mehreren XSDs beschrieben werden. Und spätestens hier tauchen dann oft Namensräume auf. Eine Analogie dazu wären die Packages bei Java. Eine Klasse Preis kann in einem Package de.beispiel.grundtypen auftauchen, eine weitere, davon vollkommen unabhängige Klasse Preis aber auch noch mal im Package de.beispiel.artikelinfos. Ebenso kann in XML ein Preis-Element als Grundtyp so definiert sein:


<s:simpleType name="Preis">
  <s:restriction base="s:float">
    <s:minInclusive value="0" />
  </s:restriction>
</s:simpleType>

Das heißt, ein Preis ist eine Kommazahl, die zwar 0 sein kann, aber niemals negativ.
So, nun kann aber die Preisinformation zu einem Artikel z.B. gleich mal Einkaufspreis, Verkaufspreis und Mehrwertsteuersatz enthalten:


<s:complexType name="Preis">
  <s:sequence>
    <s:element name="ekpreis" type="Preis" />  <!-- minOccurs und 
    maxOccurs sind nicht angegeben, dann wird für beides 
    eine 1 angenommen -->
    <s:element name="vkpreis" type="Preis" />
    <s:element name="mwst" type="s:float" />
  </s:sequence>
</s:complexType>

Der komplexe Typ nennt sich „Preis“, zwei seiner Werte sind auch vom Typ Preis, aber das ist der einfache Typ. Wenn das keine Typenverwirrung gibt…

An dieser Stelle kommen Namensräume ins Spiel. Wie so was aussieht, können Sie am Schema selbst sehen. Zum einen muss ein Namensraum deklariert werden:

<s:schema xmlns:s=“http://www.w3.org/2001/XMLSchema“ elementFormDefault=“qualified“>

Hier wird der Namensraum „s“ deklariert. Die URL ist bei Schemata einfach genau diese. Bei normalen XML-Dateien kann hier im Prinzip jede x-beliebige URL stehen, sie muss faktisch noch nicht mal wirklich irgendwohin führen. Außer, man will den Aufbau tatsächlich prüfen. Dazu am Ende noch mal mehr. Allerdings bekommt jeder Namespace seine eigene URL.

Und genutzt wird der deklarierte Namespace, indem man ihn als Präfix vor die Tag- oder Attributnamen (oder, zumindest in Schemata, auch Attributwerte) setzt:

<s:element name=“ArtNr“ type=“s:string“ minOccurs=“1″ maxOccurs=“1″/>

Sowohl das Tag „element“ als auch der Typ „string“ stecken also im Namensraum „s“, in dem ganz einfach alle Typen von Elementen und Attributen stecken, die vom WWW-Consortium (kurz W3C) für Schemata definiert sind.

Auf unser Preisbeispiel übertragen sollten wir die grundlegende Definition eines Preises (der einfach nur nicht negativ sein kann) z.B. in den Namensraum „basic“ stecken, während die Preisinformationen eines Artikels mit EK, VK und MwSt in den Namensraum „article“ kommen. Zuerst die Definition des Artikelpreises, nachdem der einfache Preis seinen Namensraum bekommen hat, danach ein Mini-Artikel:


<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
  targetNamespace="http://www.lobster.de/gibtsnicht/article.xsd"
  xmlns="http://www.lobster.de/gibtsnicht/article.xsd"
  xmlns:basic="http://www.lobster.de/gibtsnicht/basic.xsd">
<!-- Hier wurde erst mal angegeben, im welchem Schema und Namensraum die im Folgenden definierten Typen und Elemente stehen sollen. -->
<!-- Außerdem wurde schon mal angegeben, daß wir Elemente des Namensraumes "basic" nutzen werden und wo deren Definition herkommt. -->

<!-- Und nun wird genau dieses Schema basic.xsd importiert. -->
<s:import namespace="http://www.lobster.de/gibtsnicht/basic.xsd"
  schemaLocation="basic.xsd />
<!-- Zu beachten: Die URL muss exakt der im darüberstehenden Schema-Tag entsprechen, und wir gehen hier davon aus, dass die Datei basic.xsd direkt neben der article.xsd liegt. -->
...
<s:complexType name="Preis">
  <s:sequence>
    <s:element name="ekpreis" type="basic:Preis" />
    <s:element name="vkpreis" type="basic:Preis" />
    <s:element name="mwst" type="s:float" />
  </s:sequence>
</s:complexType>

<s:element name="Artikel">
  <s:element name="Name" type="s:string" />
  <s:element name="Preisinfos" type="Preis" />  <!-- Wir sind hier mit dem (komplexen!) Preis im selben Schema bzw. Namensraum unterwegs wie mit dem Artikel, daher kein Präfix -->
  <s:element name="Aktionspreis" type="basic:Preis" /> <!-- Das ist jetzt aber der andere, einfache Preis, der braucht denn auch sein Präfix -->
</s:element>
...
</s:schema>

Der Artikel wiederum steckt in einem Katalog, der den Namensraum „cat“ bekommt (lassen wir das Schema jetzt mal weg).
In der XML-Datei, die beide Schemata nutzt, steht dann am Ende so was:


<cat:Katalog xmlns:cat="http://www.lobster.de/gibtsnicht/catalog.xsd">
<!-- Hier wird gleich im ersten Element, das den Namensraum "cat" nutzt, 
dieser auch deklariert -->
...
  <article:Artikel xmlns:article="http://www.lobster.de/gibtsnicht/article.xsd"> 
  <!-- Und nun wird auch der Namensraum "article" deklariert -->
    <article:Name>USB Stick 32G</article:Name>
    <article:Preis>
      <article:ekpreis>5.50</article:ekpreis>  <!-- Der Preis aus "basic:" 
      ist hier nur noch die Zahl, daher sieht man hier nichts mehr vom Namensraum -->
      <article:vkpreis>17.99</article:vkpreis>
      <article:mwst>19.0</article:mwst>
    </article:preis>
    <article:Aktionspreis>13.49</article:Aktionspreis>
  </article:Artikel>
...
</cat:Katalog>

Wenn man nun tatsächlich diese XML-Datei validieren, also auf einen sauberen Aufbau prüfen will, müssen sich unter den angegebenen URLs auch die Schema-Dateien finden. Lässt man die Validierung weg, kann da im Prinzip jeder Unsinn drinstehen, solange er nur wie eine URL aussieht.
Lassen wir es hierbei bewenden. Zum Thema Namespaces gibt es reihenweise ausführliche Darstellungen im Netz. Sie müssen einfach nur wissen, dass es so etwas gibt und wofür diese Präfices stehen.