apply-templates und Element "überspringen"
André Laugks
- xsl
0 fjh0 André Laugks0 André Laugks0 fjh
Hallo!
Es ist folgendes XML-Dokument gegeben.
<?xml version="1.0" encoding="iso-8859-1"?>
<liste>
<eintrag>
<vorname>André</vorname>
<nachname>Laugks</nachname>
</eintrag>
<eintrag>
<vorname>Werner</vorname>
<nachname>Otto</nachname>
</eintrag>
</liste>
Mit folgender XSLT-Datei bekomme ich alle Kind-Element ausgegeben.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template>
<xsl:template match="eintrag">
<b><xsl:value-of select="."/></b>
</xsl:template>
</xsl:stylesheet>
Sobald ich aber <xsl:apply-templates select="eintrag"/> nehme, bekomme ich nichts mehr ausgegeben. Ich wähle doch den Knoten "eintrag" aus?
So bekomme ich wieder was ausgegeben.
<xsl:template match="/liste">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>
Aber <xsl:template match="/"> bezieht sich doch auf das Root-Element "liste".
So möchte ich mir nur die Vornamen ausgeben lassen. Ich bekomme natürlich auch nichts ausgegeben. Kann ich in dem Pfad nicht springen. Muß ich immer komplet (/liste/eintrag/vorname) referenzieren?
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="vorname"/>
</body>
</html>
</xsl:template>
<xsl:template match="vorname">
<b><xsl:value-of select="."/></b>
</xsl:template>
</xsl:stylesheet>
Irgendwie ist mir das heute erst aufgefallen.
MfG, André Laugks
L-Andre @ gmx.de
Hallo André,
Es ist folgendes XML-Dokument gegeben.
XML-Datei
<?xml version="1.0" encoding="iso-8859-1"?>
<liste>
<eintrag>
<vorname>André</vorname>
<nachname>Laugks</nachname>
</eintrag>
<eintrag>
<vorname>Werner</vorname>
<nachname>Otto</nachname>
</eintrag>
</liste>Mit folgender XSLT-Datei bekomme ich alle Kind-Element ausgegeben.
XSLT-Datei
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template><xsl:template match="eintrag">
<b><xsl:value-of select="."/></b>
</xsl:template></xsl:stylesheet>
Du gibts den Zeichenkettenwert für jedes Eintragelement aus. Der Zeichenkettenwert eines Knoten, der Kindelemente hat, ist die aus den Inhalten der Kindelementen zusammengesetzte Zeichenkette. Somit erhälst du die Ausgabe von Vorname und Nachname jeweils eingeschlossen durch <b>-Tags.
Sobald ich aber <xsl:apply-templates select="eintrag"/> nehme, bekomme ich nichts mehr ausgegeben. Ich wähle doch den Knoten "eintrag" aus?
So bekomme ich wieder was ausgegeben.
<xsl:template match="/liste">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>Aber <xsl:template match="/"> bezieht sich doch auf das Root-Element "liste".
Nein es bezieht sich auf den Wurzel*knoten*, eine abstrakte Wurzel, deren einziges Kind das Wurzel*element* <liste> ist. Du musst daher dieses Element auch noch ansteuern.
So möchte ich mir nur die Vornamen ausgeben lassen. Ich bekomme natürlich auch nichts ausgegeben. Kann ich in dem Pfad nicht springen. Muß ich immer komplet (/liste/eintrag/vorname) referenzieren?
Nein "Springen" geht so nicht, ausser über die eingebauten Template-Rules, aber auch das ist kein Springen, sondern ein Standardverhalten, für den undefinierten Fall.
Du kannst höchstens mit benannten Templates arbeiten und die dann wie Funktionen irgendwo in einem Template aufrufen.
Gruß
Franz
Hallo Franz!
Somit erhälst du die Ausgabe von Vorname und Nachname jeweils eingeschlossen durch <b>-Tags.
Ich wollte sehen, welchen Text/Textknoten wie "genommen" wird.
Sobald ich aber <xsl:apply-templates select="eintrag"/> nehme, bekomme ich nichts mehr ausgegeben. Ich wähle doch den Knoten "eintrag" aus?
Nein es bezieht sich auf den Wurzel*knoten*, eine abstrakte Wurzel, deren einziges Kind das Wurzel*element* <liste> ist. Du musst daher dieses Element auch noch ansteuern.
Und da setzt es manchmal bei mir aus.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template>
<xsl:template match="eintrag">
<b><xsl:value-of select="."/></b>
</xsl:template>
</xsl:stylesheet>
Es wird durch den Baum gewandert. Sobald xsl:apply-templates/ auf ein Temlate stößt, welches zu einem Konten im Baum gehört, wird es angewendet. Überspringt xsl:apply-templates/ die Elemente "liste" und "eintrag"? Ich sage mal ja.
Oder...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template>
<xsl:template match="vorname">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Hier bekomme ich "André Laugks Werner Otto" ausgegeben. Ich steuere doch jeweils nur das Element "vorname" an bzw. habe dafür ein Template definiert.
Mit ...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template>
<xsl:template match="eintrag">
<xsl:value-of select="vorname"/>
</xsl:template>
</xsl:stylesheet>
... bekomme ich die Vornamen ausgegeben. Da hackt es noch.
Aber so ...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>
<xsl:template match="eintrag">
<xsl:value-of select="vorname"/>
</xsl:template>
</xsl:stylesheet>
... selectiere ich direkt das Element "eintrag". Mmmmhhhhhhh....
Um noch mal die Pfade klar zu stellen?!
<?xml version="1.0" encoding="iso-8859-1"?>
-> /
<?xml version="1.0" encoding="iso-8859-1"?>
<liste>
</liste>
-> /liste
<?xml version="1.0" encoding="iso-8859-1"?>
<liste>
<eintrag>
</eintrag>
<eintrag>
</eintrag>
</liste>
-> /liste/eintrag
<?xml version="1.0" encoding="iso-8859-1"?>
<liste>
<eintrag>
<vorname></vorname>
<nachname></nachname>
</eintrag>
<eintrag>
<vorname></vorname>
<nachname></nachname>
</eintrag>
</liste>
-> /liste/eintrag/vorname
-> /liste/eintrag/nachname
Du kannst höchstens mit benannten Templates arbeiten und die dann wie Funktionen irgendwo in einem Template aufrufen.
Ja, daran habe ich dann gestern nach dem posten auch gedacht. Für mich vielleicht leichter.
Vielleicht denke ich noch zu sehr an JavaScript, wo man "Element" direkt ansprechen kann.
MfG, André Laugks
L-Andre @ gmx.de
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>
Ok, ich glaube jetzt hat's klick gemacht.
Klar, den Pfad /eintrag gibt es nicht, aber den Pfad /liste/eintrag. Da es /eintrag nicht gibt, wird auch nichts angezeigt.
Aber warum mir bei diesem Template auch die Nachnamen angezeigt werden, ist mir noch nicht ganz klar.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template>
<xsl:template match="vorname">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
MfG, André Laugks
L-Andre @ gmx.de
Hallo André,
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>Ok, ich glaube jetzt hat's klick gemacht.
Klar, den Pfad /eintrag gibt es nicht, aber den Pfad /liste/eintrag. Da es /eintrag nicht gibt, wird auch nichts angezeigt.
Yep, so ist es. Der Kontext ist der hier der Wurzelknoten und ausgehend von diesem kannst du Dein Eintrag Element nur über liste/eintrag ansteuern.
Oder so:
<xsl:template match="/liste">
<html>
<body>
<xsl:apply-templates select="eintrag"/>
</body>
</html>
</xsl:template>
Aber warum mir bei diesem Template auch die Nachnamen angezeigt werden, ist mir noch nicht ganz klar.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
xsl:apply-templates/
</body>
</html>
</xsl:template><xsl:template match="vorname">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Ich denke es fehlt eigentlich nur ein Baustein in Deinem Verständnis des Transformationsprozesses: das sind die sogenannten eingebauten Template Rules. Die werden immer dann angewendet, wenn der XSLT-Prozessor beim Abarbeiten des Baumes keine passende Template Rule für einen Knoten findet. "Abarbeiten" bedeutet nichts anderes als beim Wurzelknoten beginnen und im Stylesheet nach einer Template Rule für diesen Knoten suchen. Bei deinem obigen Beispiel findet er eine:
<xsl:template match="/">
</xsl:template>
Hättest du geschrieben
<xsl:template match="/liste">
</xsl:template>
hätte er keine gefunden. Was macht nun der XSLT-Prozessor? Er wendet die besagten standardisierten Template Rules an. Für die verschiedenen Knotentypen des XSLT-Datenmodells gibt es verschiedene eingebaute Template Rules:
Wurzelknoten Aufruf von xsl:apply-templates, um die Kinder des Wurzelknotens zu bearbeiten
Element Aufruf von xsl:apply-templates, um die Kinder des gegenwärtigen Knotens zu bearbeiten
Attribut Kopieren des Attributwertes in den Ergebnisbaum (als Text)
Text Kopieren des Textes in den Ergebnis-baum
Kommentar Tue nichts
Verarbeitungs- Tue nichts
anweisung
Namensraum Tue nichts
Findet er also keine Template Rule für den Wurzelknoten ruft er xsl:apply-templates/ auf. Das bewirkt (ohne das select-Attribut), dass alle Kindknoten des Wurzelknotens (immer nur einer, bei Dir <liste>)) selektiert und abgearbeitet werden, d.h. passende Template Rules gesucht werden. Für <liste> wird dann eine gefunden.
Zurück zum eigentlichen Beispiel mit den Vornamen. Der XSLT-Prozessor findet also eine Template Rule für den Wurzelknoten und kopiert die literalen Ergebniselemente in den Ergebnisbaum bis er auf die Anweisung xsl:apply-templates/ stößt. Diese veranlasst ihn alle Kindknoten des Wurzelknotens zu selektieren und Template Rules dafür zu suchen. Der einzige Kindknoten des Wurzelknotens ist das <liste>-Element. Dafür findet er keine Template Rule. Also wendet er die eingebaute Template Rule für Elementknoten an. Diese hat den gleichen Effekt wie die eingebaute Template Rule für den Wurzelknoten: es wird xsl:apply-templates/ aufgerufen. Somit werden alle Kinder des <liste>-Elements selektiert und Template Rules für diesen Knoten gesucht. Auch hierfür werden keine gefunden => eingebaute Template Rule für Elementknoten. Die Kinder eines <eintrag>-Elements sind die Elemente <vorname> und <nachname>. Für <vorname> gibt es eine Template Rule, die besagt "Schreibe den Inhalt des Elements in den Ergebnisbaum". Das wird getan und so steht also der Vorname da. Nun wird nach einer Template Rule für das <nachname>-Element gesucht, aber keine gefunden. Also wieder Aufruf der eingebauten Template Rule für Elementknoten und somit Abarbeitung der Kindknoten des Elements. Diesmal aber ist der einzige Kindknoten ein Textknoten mit dem Inhalt des <nachname>-Elements. Für Textknoten findet der XSLT-Prozessor aber auch keine Template Rules. Daher wird nun die eingebaute Template Rule für Textknoten aufgerufen. Und siehe da, diese lautet "Kopieren des Textes in den Ergebnisbaum". So landet also der Nachname in der Ausgabe.
Verhindern kannst du dieses rekursive Hinabsteigen beim Aufruf von xsl:apply-templates/ im Wurzelelement durch eine leere Template Rule für Textknoten:
<xsl:template match="text()" />
oder du steuerst wirklich nur die Vornamen an:
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="liste/eintrag/vorname"/>
</body>
</html>
</xsl:template>
Gruß
Franz