Teil von SELFHTML aktuell Teil von Artikel Teil von XML/XSL/XML-basierte Sprachen

XML/XSL/XML-basierte Sprachen:
Elemente gruppieren durch Referenzierung von ID-Werten

nach unten Thomas J. Sebestyen
nach unten Hinweise zum Thema
nach unten Beispiel mit Erläuterungen
nach unten Erläuterung zu XML und DTD
nach unten Erläuterung zu XSLT
nach unten Weiterführende Links

Thomas J. Sebestyen

E-Mail: E-Mail thomas.js@selfhtml.org
Homepage-URL: deutschsprachige Seite http://www.meta-text.net

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

nach obennach unten

Hinweise zum Thema

Im vorliegenden Artikel wird der oft gestellten Frage nachgegangen, wie man auf die in einer XML-Datei als ID notierten Attribute zugreifen und diese für die Referenzierung und Gruppierung von Elementen bei der Ausgabe verwenden kann. Für das Verständnis dieses Artikels sind Grundkenntnisse in bereichsübergreifendes Kapitel XML und bereichsübergreifendes Kapitel XSLT Voraussetzung.

nach obennach unten

Beispiel mit Erläuterungen

Beispiel

Das Beispiel zeigt eine XML-Datei, in der verschiedene Veranstaltungen und die Teilnehmer, die diese besuchen, aufgelistet sind. Die Beziehung zwischen den Veranstaltungen und den Teilnehmern wird durch die Attribute id bereichsübergreifende Seite (ID) und events bereichsübergreifende Seite (IDREF) hergestellt.

Die gewünschte Ausgabe sollte einerseits die jeweilige Veranstaltung und ihre Teilnehmer auflisten, anderseits sollte es auch eine Liste der Teilnehmer mit den von ihnen besuchten Veranstaltungen geben.

Popup-Seite Anzeigebeispiel: So sieht's aus (Datei veranstaltungen.htm)
Popup-Seite Anzeigebeispiel: So sieht's aus (Datei veranstaltungen.xml - XML/XSLT-fähiger Browser erforderlich)

Beispiel-DTD veranstaltungen.dtd:

<!ELEMENT activity (events,participants)>
<!ELEMENT events (event)+>
<!ELEMENT event EMPTY>
<!ATTLIST event
   id   ID    #REQUIRED
   name CDATA #REQUIRED>
<!ELEMENT participants (participant)+>
<!ELEMENT participant EMPTY>
<!ATTLIST participant
   events IDREFS  #REQUIRED
   name   CDATA   #REQUIRED>

Beispiel-XML veranstaltungen.xml:

<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="veranstaltungen.xsl"?>
<!DOCTYPE activity SYSTEM "veranstaltungen.dtd">
<activity>
  <events>
     <event id="e01" name="XML on Stage"/>
     <event id="e02" name="SELFHTML Treffen"/>
     <event id="e03" name="Expertenchat"/>
  </events>
  <participants>
    <participant events="e02" name="Otto Lieb"/>
    <participant events="e02" name="Axel Bravo"/>
    <participant events="e01 e03" name="Petra Klug"/>
    <participant events="e02 e03" name="Birgit Lob"/>
    <participant events="e01 e03" name="Andrea Groß"/>
    <participant events="e01 e02 e03" name="Heinrich Aller"/>
    <participant events="e01 e02 e03" name="Jessy Keen"/>
  </participants>
</activity>

Beispiel-XSL veranstaltungen.xsl:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
   <xsl:output 
      method="html"
      encoding="iso-8859-1"
      doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
      indent="yes" />
   
   <xsl:key name="participant2event" match="participant" use="id(@events)/@id" />

   <xsl:template match="/activity">
   <html>
      <head>
         <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
         <title>Veranstaltungen und Teilnehmer</title>
      </head>
      <body>
         <h1>Veranstaltungen und Teilnehmer</h1>
         <h2>Pro Veranstaltung</h2>
         <xsl:apply-templates select="events"  />
         <hr />
         <h2>Pro Teilnehmer</h2>
         <xsl:apply-templates select="participants" />
      </body>
   </html>
   </xsl:template>

   <xsl:template match="event">
       <h3><xsl:value-of select="@name"/></h3>
       <ul>
           <xsl:apply-templates select="key('participant2event', @id)" mode="byevent"/>
       </ul>
   </xsl:template>
   <xsl:template match="participant">
       <p>
          <strong><xsl:value-of select="@name"/>: </strong>
         <xsl:apply-templates select="id(@events)" mode="byparticipant"/>
      </p>
   </xsl:template>

   <xsl:template match="event" mode="byparticipant">
      <xsl:value-of select="@name"/>
      <xsl:if test="position() != last()">, </xsl:if>
   </xsl:template>
   <xsl:template match="participant" mode="byevent">
      <li><xsl:value-of select="@name"/></li>
   </xsl:template>
   
</xsl:stylesheet>

Erläuterung zu XML und DTD

In unserem Beispiel verwenden wir eine komplette DTD, in der Praxis ist es jedoch oft der Fall, dass für die XML-Datei zwar keine DTD existiert, dennoch Elemente mit Attributen versehen werden, die eine ID-Funktion erfüllen bzw. erfüllen sollen. Aus der Sicht des XSL-Prozessors ist es nicht nötig, dass eine komplette DTD vorhanden ist. Es genügt lediglich, dem Prozessor mitzuteilen, welches Attribut vom Typ ID ist. So können Sie in unserem Beispiel-XML, statt auf eine externe DTD zu verweisen, eine interne DTD-Untermenge verwenden.

<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="veranstaltungen.xsl"?>
<!DOCTYPE activity [
   <!ELEMENT event EMPTY>
   <!ATTLIST event id   ID    #REQUIRED>
]>
<activity>
...
</activity>

Beachten Sie:

Falls Sie einen nicht-validierenden XML-Parser verwenden (wie dies z.B. bei Mozilla der Fall ist) und weil ein XSL-Prozessor sich nicht um die um die Validität der XML-Datei kümmert, ist es sogar möglich, ungültige ID-Werte (z.B. Werte, die mit einer Zahl beginnen) zu notieren. Es ist jedoch davon abzuraten, diese Möglichkeit in der Praxis zu nutzen, denn beim Umstieg auf einen validierenden XML-Parser wird dieser solche Fehler melden und die Transformation abbrechen.

Da der Mozilla Browser einen nicht-validierenden XML-Parser verwendet und somit keine extenen Entities (wie etwa eine DTD) auflösen kann, müssen Sie - falls Sie direkt Ihre XML-Dateien im Internet anbieten - für diesen Browser die bereichsübergreifende Seite Attribut-Deklaration auf alle Fälle (also auch dann, wenn Sie eine externe DTD benutzen!) in der internen DTD-Untermenge notieren.

Erläuterung zu XSLT

In unserem XSL definieren wir zuerst einen bereichsübergreifende Seite key, der auf die participant-Elemente im XML zugreift und als Wert des Schlüssels den bereichsübergreifende Seite XPath-Ausdruck id(@events)/@id verwendet.

Dieser Ausdruck verwendet die bereichsübergreifende Seite id()-Funktion für das Attribut events im participant-Element. Dadurch werden die in diesem Attribut angegebenen Werte als ID-Referenzen auf andere Elemente im XML-Dokument erkannt. Durch die Angabe /@id wird dann die Verknüpfung zu den referenzierten Elementen, genauer gesagt mit deren id-Attribut, hergestellt. Somit enthält dieser key für jedes participant-Element das referenzierte event-Element bzw. die referenzierten event-Elemente.

Die nächsten wichtigen Schritte sind, die bereichsübergreifende Seite Templates für die beiden Elemente event und participant zu definieren.

<xsl:template match="event">
    <h3><xsl:value-of select="@name"/></h3>
    <ul>
        <xsl:apply-templates select="key('participant2event', @id)" mode="byevent"/>
    </ul>
</xsl:template>

In diesem Template wird nach der Ausgabe des Namens des events eine Aufzählungsliste erstellt. In dieser Liste wird dann mit Hilfe des bereichsübergreifende Seite apply-templates-Elements ein anderes Template mit dem Modus "byevent" instanziiert.

Das select-Attribut in diesem apply-templates-Element benutzt die XPath-Funktion bereichsübergreifende Seite key(). Diese greift auf unseren, mit dem Namen participant2event definierten, key zu und als Vergleichs- oder Referenzwert benutzt sie dabei das id-Attribut im gegenwärtigen event-Element.

Das bedeutet, dass hier ein Template mit dem Modus "byevent" für diejenige participant-Elemente gesucht und ausgeführt wird, in deren event-Attribut der Wert des id-Attributs des gegenwärtigen event-Elements vorhanden ist. Beim event "XML on Stage" sind dies die Elemente:

<participant events="e01 e03" name="Petra Klug" />
<participant events="e01 e03" name="Andrea Groß" />
<participant events="e01 e02 e03" name="Heinrich Aller" />
<participant events="e01 e02 e03" name="Jessy Keen" />

Das Template für diese Elemente erstellt nur einen Listenpunkt, der den Namen des Teilnehmers enthält:

<xsl:template match="participant" mode="byevent">
   <li><xsl:value-of select="@name"/></li>
</xsl:template>

Hinweis:

Ein Template wird für jedes Element, auf das es zutrifft, neu instanziiert, deshalb ändert sich das Ziel-Element bei jedem "Durchlauf" des Templates und aus diesem Grund spricht man in diesem Zusammenhang vom gegenwärtigen Element, also von dem Element, das gerade abgearbeitet wird. Instanziieren bedeutet, dass alle Elemente und Attribute, die innerhalb des entsprechenden (z.B.) xsl:template-Elements stehen und nicht zum XSLT-Namensraum gehören, in den Ergebnisbaum geschrieben werden.

Das Template für das participant-Element sieht etwas anders aus:

<xsl:template match="participant">
    <p>
       <strong><xsl:value-of select="@name"/>: </strong>
      <xsl:apply-templates select="id(@events)" mode="byparticipant"/>
   </p>
</xsl:template>

Hier wird für jedes participant-Element ein Absatz erzeugt und der Name des Teilnehmers in fetter Schrift ausgegeben. Danach wird wiederum ein apply-templates-Element, diesmal mit dem Modus "byparticipant", aufgerufen. Gesucht und ausgeführt wird ein Template für event-Elemente durch die id()-Funktion, die im event-Attribut des gegenwärtigen participant-Elements referenziert ist. Beim Teilnehmer "Petra Klug" sind dies die Elemente:

<event id="e01" name="XML on Stage" />
<event id="e03" name="Expertenchat" />

Das Template für diese Elemente gibt einfach den Namen der Veranstaltung aus und wenn diese nicht die letzte in der Reihe ist, fügt es nach dem Namen ein Komma und ein Leerzeichen ein.

<xsl:template match="event" mode="byparticipant">
   <xsl:value-of select="@name"/>
   <xsl:if test="position() != last()">, </xsl:if>
</xsl:template>

Das Template für das activity-Element (<xsl:template match="/activity">) enthält das Grundgerüst für die HTML-Seite und ruft an den entsprechenden Stellen die normalen Templates für Veranstaltungen und Teilnehmer auf.

nach obennach unten

Weiterführende Links

Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.

bereichsübergreifendes Kapitel SELFHTML: Darstellung von XML-Daten
deutschsprachige Seite XSL Transformations (XSLT) Version 1.0 Deutsche Übersetzung
deutschsprachige Seite XML Path Language (XPath) Version 1.0 Deutsche, kommentierte Übersetzung

Teil von SELFHTML aktuell Teil von Artikel Teil von XML/XSL/XML-basierte Sprachen

© 2007 bereichsübergreifende Seite Impressum