XSL für Dummies
RM
- xsl
0 ritschmanhard0 Roli0 ritschmanhard0 ritschmanhard0 Cyx23
Hallo,
Ich hab einfach mit der Transformation von XML nach Text mittels XSL Probleme. Ich habe nachfolgende XML Struktur:
<feed>
<produkte>
<produkt>
<myid>1</myid>
<name>Test</name>
</produkt>
<produkt>
<myid>2</myid>
<name>Test2</name>
</produkt>
</produkte>
<preise>
<preis>
<myid>1</myid>
<akt>22.00</akt>
</preis>
<preis>
<myid>2</myid>
<akt>100.00</akt>
</preis>
</preise>
</feed>
Wie kann ich nun mittels XLS eine Liste generieren die nachfolgend aussieht:
1 Test 22.00
1 Test2 100.00
Das heisst ich möchte die myid als Schlüssel verwenden, um den Preis für ein Produkt auszugeben.
Ich hab schon einige Dokus gelesen, bin aber einfach nicht schlau daraus geworden.
Herzlichen Dank für Eure Hilfe.
Hallo Roli!
Zunächst ein paar kurze Anmerkungen:
Klar ist wohl, dass die XML Struktur nicht optimal ist;
Gedanklich ist Preis ja wohl ein Attribut des Produkts, also:
<produkt id="1" preis="200" name="super">
alternativ:
<produkt id="1">
<preis>200</preis>
<name>super</name>
</produkt>
sind sehr viel logischer...
Aber wahrscheinlich handelt es sich hier um eine Datenbank-Exzerpt,
"und dann ist halt die Relation der ID Knoten...".
Na gut - aber ich setze wenigstens voraus, dass der myid Konten der erste in der Liste ist und alle anderen attributknoten folgen.
Die Lösung sieht so aus:
<?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>
<head>
</head>
<body>
<table border="1">
<xsl:for-each select="//produkt/myid">
<xsl:call-template name="Schleife">
<xsl:with-param name="Ident" select="." />
</xsl:call-template>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="Schleife">
<xsl:param name="Ident" />
<tr>
<td>ident: <xsl:value-of select="$Ident"/></td>
<xsl:for-each select="//*/myid">
xsl:choose
<xsl:when test="$Ident = ./.">
<xsl:for-each select="./following-sibling::*">
<td>
<xsl:value-of select="./."/>
</td>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Hilfreich sind hierbei http://www.zvon.org/xxl/XPathTutorial/Output/example17.html und alle anderen Knotenbeschreiber sowie http://de.selfhtml.org/xml/darstellung/xsltelemente.htm#with_param
Viele Grüsse,
Richard
Hallo Richard,
Vielen herzlichen Dank für Deine Erklärung und das Beispiel.
Mir ist bewusst, dass die XML Struktur nicht ideal ist, aber ich kriege die aus einem Feed und kann an der Struktur nix ändern. Die Struktur hat für mich das ganze noch unverständlicher gemacht.
Ich hab das Beispiel studiert und einigermassen verstanden hoffe ich ;-)
Wenn jetzt meine beiden Listen noch mehrere Attribute hätten, würde das Beispiel diese auch ausgeben oder hab ich das falsch verstanden?
Wenn ich sichergehen möchte, dass aus den Listen nur Name und Preis ausgegeben werden, wäre dann meine Variante auch korrekt ? Diese Frage nur zum Verständis ob ich das einigermassen kapiert habe ;-)
Nochmals vielen Dank für die rasche Hilfe
Roli
<?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>
<head>
</head>
<body>
<table border="1">
<xsl:for-each select="//produkt/myid">
<xsl:call-template name="SchleifeProdukt">
<xsl:with-param name="Ident" select="." />
</xsl:call-template>
<xsl:call-template name="SchleifePreis">
<xsl:with-param name="Ident" select="." />
</xsl:call-template>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="SchleifeProdukt">
<xsl:param name="Ident" />
<tr>
<td>ident: <xsl:value-of select="$Ident"/></td>
xsl:choose
<xsl:when test="$Ident = /produkte/produkt/myid">
<td>
<xsl:value-of select="/produkte/produkt/name"/>
</td>
</xsl:when>
</xsl:choose>
</tr>
</xsl:template>
<xsl:template name="SchleifePreis">
<xsl:param name="Ident" />
<tr>
<td>ident: <xsl:value-of select="$Ident"/></td>
xsl:choose
<xsl:when test="$Ident = /preise/preis/myid">
<td>
<xsl:value-of select="/preise/preis/akt"/>
</td>
</xsl:when>
</xsl:choose>
</tr>
</xsl:template>
</xsl:stylesheet>
Hi Roli!
Wenn jetzt meine beiden Listen noch mehrere Attribute hätten, würde das Beispiel diese auch ausgeben oder hab ich das falsch verstanden?
Ja, es werden die Inhalte aller Knoten ausgegeben, die in der Hierarchie-ebene auf dem Level des <myid> (Randbedingung: nach myid) Knotens liegen.
Wenn ich sichergehen möchte, dass aus den Listen nur Name und Preis ausgegeben werden, wäre dann meine Variante auch korrekt ?
Nein, wäre sie (leider) nicht:
Dieser Block ist noch OK: Ermittle für jedes <produkt> den Inhalt des <myid> Knotens und rufe damit die Schleife(n) auf:
<xsl:for-each select="//produkt/myid">
<xsl:call-template name="SchleifeProdukt">
<xsl:with-param name="Ident" select="." />
</xsl:call-template>
<xsl:call-template name="SchleifePreis">
<xsl:with-param name="Ident" select="." />
</xsl:call-template>
</xsl:for-each>
Jetzt wird's leider falsch (beide Schleifen gleich...):
<xsl:template name="SchleifeProdukt">
<xsl:param name="Ident" />
<tr>
<td>ident: <xsl:value-of select="$Ident"/></td>
xsl:choose
<xsl:when test="$Ident = /produkte/produkt/myid">
<td>
<xsl:value-of select="/produkte/produkt/name"/>
</td>
</xsl:when>
</xsl:choose></tr>
</xsl:template>
Du forderst hier: WENN die oben ermittelte myid übereinstimmt mit dem _ersten_ Knoten, der der Form /produkte/produkt/myid genügt, dann gib den Inhalt des _ersten: Knotens aus, der der Form /produkte/produkt/name genügt.
Somit wird durch dein Beispiel mit jeder myid gegen den ersten Knoten des xml Dokuments abgeglichen und wenn Gleichheit der myid besteht (nur einmal der Fall) dann wird dieser Knoten ausgegeben.
Die korrekte Modifikation meines Beispiels müsste lauten - unter der Voraussetzung, dass name und akt nur einmal vorkommen und nicht sortiert wird (d.h. wenn der akt Knoten vor dem name Knoten kommt, wird er zuerst ausgegeben)
...
<xsl:when test="$Ident = ./.">
<xsl:for-each select="./following-sibling::*[name()='name' or name()='akt']">
<td>
<xsl:value-of select="./."/>
</td>
</xsl:for-each>
</xsl:when>
...
Sollte eine Sortierung fällig werden, so weise ich noch drauf hin, dass bei dem <xsl:for-each select="./following-sibling::*[name()='name' or name()='akt']">
eine <xsl:sort ...> Konstruktion folgen könnte; diese ist aber komplex (kann ich nicht aus dem Ärmel schütteln) und hat dann auch nicht
Name1 Preis1
Name2 Preis2
als Folge, sondern
Name1 Name2
Preis1 Preis2
Viels Spass,
Richard
Hi Roli!
Sollte eine Sortierung fällig werden, so weise ich noch drauf hin, dass bei dem <xsl:for-each select="./following-sibling::*[name()='name' or name()='akt']">
eine <xsl:sort ...> Konstruktion folgen könnte...
Wenn alphabetisch absteigend nach Elternknotennamen sortiert werden soll - also alphabetisch pro(dukt) vor pre(is) , dann sieht dieser sort wie folgt aus:
...
<xsl:for-each select="//*/myid">
<xsl:sort select="name(./parent::*)" order="descending" data-type="text"/>
Das Eergebnis ist dann:
name1 akt1
name2 akt2
beachte: es wird nicht nach name und akt, sondern nach produkt und preis sortiert...
So, jetzt ist es aber gut - oder?
Viele Grüsse,
Richard
Hallo,
So, jetzt ist es aber gut - oder?
da interessiert mich mal der Vergleich mit diesem
Beispiel Katalog mit zwei gleich aufgebauten Datenquellen.
Hast du inzwischen eine XML-Datei mit allen Daten, oder noch eine separate
Preisliste und dazu einen Produktkatalog, verknüpft über die Artikelnummern?
Grüsse
Cyx23