Anja: mit xsl zu csv konvertieren

Liebe Leute,
ich möchte gerne eine xml-datei folgender Struktur:
<exportData>
<Book>
<field name="title">a</field>
<field name="authors">b</field>
<field name="genre">c</field>

</Book>

<Book>
<field name="title">d</field>
<field name="authors">e</field>
<field name="genre">f</field>

</Book>
</exportData>

mittels einer xsl datei zu volgendem konvertieren:

a;b;c d;e;f

Die erstellte CSV-Import-Datei muss die UNIX-Textcodierung unterstützen, bei der ein Zeilenumbruch durch die Zeichenkombination LF dargestellt wird.
Leider habe ich von xml (noch) wenig Ahnung. Kann mir jemand helfen wie die xsl datei auszusehen hat?

Grüße,
Anja

  1. Hallo,

    mittels einer xsl datei zu volgendem konvertieren:
    a;b;c d;e;f

    CSV hat doch eher die Konvention, dass ein Zeilenumbruch zwischen Zeilen stattfinden muss? So also:

    ~~~csv a;b;c
      d;e;f

      
    Du willst also pro Book-Elemente eine Zeile haben und den Inhalt der field-Elemente getrennt durch Semikolen (kolons? kola?) haben.  
      
      
    
    > Die erstellte CSV-Import-Datei muss die UNIX-Textcodierung unterstützen, bei der ein Zeilenumbruch durch die Zeichenkombination LF dargestellt wird.  
      
    Sonst bräuchtest Du ja kein LF.  
      
      
    
    > Leider habe ich von xml (noch) wenig Ahnung. Kann mir jemand helfen wie die xsl datei auszusehen hat?  
      
    Es ist immer doof, Fertiglösungen anzubieten, die nicht verstanden werden, also hier mal simpel aufgebaut:  
      
      ~~~xml
    <?xml version="1.0"?>  
      <xsl:stylesheet version="1.0"  
                      xmlns="http://www.w3.org/1999/XSL/Transform">  
      <xsl:output method="text"/>  
      <xsl:strip-space elements="*"/>  
      
      <xsl:template match="/exportData/Book">  
        <xsl:text>bla</xsl:text>  
        <xsl:text>;</xsl:text>  
        <xsl:text>bla</xsl:text>  
        <xsl:text>;</xsl:text>  
        <xsl:text>bla</xsl:text>  
        <xsl:text>;</xsl:text>  
        <xsl:text>&#xA;</xsl:text>  
      </xsl:template>  
      
      </xsl:stylesheet>
    

    XSLT Stylesheets sind auch XML Dokumente im XML Namensraum „http://www.w3.org/1999/XSL/Transform“ und eingepackt in das Root-Element. Normalerweise baut mal mit XSLT ein neues XML-Dokument auf, d.h man erstellt XML-Elemente und Attribute und Textknoten. Beim Verarbeiten wird dann ein XML-Dokument ausgespuckt. Da Du Text ausgeben willst, gibt es noch zwei Anweisungen zum Verarbeiten.

    • Das Element xsl:output sagt hier, dass das Resultat hier als Text ausgegeben wird, d.h. nur die Textknoten im Resultatdokument werden zu Text verwandelt und gespeichert.
    • Das Element xsl:strip-space sorgt dafür, dass aller („*“) unnötiger Weissraum im Eingabedokument ignoriert werden soll, man kann den nicht im Resultatdokument brauchen.

    XSLT arbeitet im wesentlichen nach dem Prinzip, Elemente im Eingabedokument zu finden und darauf Templates anzuwenden. xsl:template in obigem Stylesheet ist ein solches Template.

    Finden tut man in XML-Dokumenten Dinge mit der Sprache XPath, die u.a. Dinge adressieren kann. Der Attributwert "/exportData/Book" ist ein solcher XPath-Ausdruck. Der / steht für das Eingabedokument, exportData“ sagt, dass wir nach Elementen mit dem Namen „exportData“, also Deinem Root-Element suchen, der nächste Schrägstrich deutet Kindelemente von exportData an und [code lang=xpath]Book sucht dann also nach Book-Elementen. Der Ausdruck gibt also die Book-Elemente zurück, die Kindelemente von exportData-Elementen sind, die Root-Elemente von Dokumenten sind.

    Auf Book-Elemente wird nun das Template angewandt, das ist der Inhalt des Template-Elementes. Das Element xsl:text erstellt einen Text-Knoten, d.h. der Inhalt davon wird im Resultatdokument einfach als Text ausgegeben. &#xA; ist die numerische Notation (ein numerisches Entity) für LF, der unixoide Zeilenumbruch. Es werden also sieben Text-Knoten erstellt. Man kann auch alles in einen Text-Knoten schreiben, ich hab es aber für den zweiten Schritt aufgeteilt.

    Wenn man obiges Stylesheet auf Dein Eingabedokument anwendet, kommt diese Ausgabe raus:

    ~~~csv bla;bla;bla;
      bla;bla;bla;

      
    Der XPath-Ausdruck gibt die zwei Elemente zurück, auf die er passt, also die beiden Book-Elemente in Deinem Eingabedokument. Pro Book-Element wird das Template angewandt, das dann drei durch Semikolons getrennte „bla“ und einen Zeilenumbruch ausgibt. Das sind schon mal Deine benötigten Zeilen pro Book-Element und ein „bla“ für die Field-Elemente. „bla“ ist natürlich unspannend, also ersetzen wir die mal durch den richtigen Inhalt.  
      
      ~~~xml
    <?xml version="1.0"?>  
      <xsl:stylesheet version="1.0"  
                      xmlns="http://www.w3.org/1999/XSL/Transform">  
      <xsl:output method="text"/>  
      <xsl:strip-space elements="*"/>  
      
      <xsl:template match="/exportData/Book">  
        <xsl:value-of select="field[@name = 'title']"/>  
        <xsl:text>;</xsl:text>  
        <xsl:value-of select="field[@name = 'authors']"/>  
        <xsl:text>;</xsl:text>  
        <xsl:value-of select="field[@name = 'genre']"/>  
        <xsl:text>&#xA;</xsl:text>  
      </xsl:template>  
      
      </xsl:stylesheet>
    

    Das „bla“ ist rausgeflogen, das überflüssige Semikolon am Ende der Zeile auch, stattdessen ist ein neues Element namens xsl:value-of da. Es fügt also einen Wert ein. Diesen Wert kriegt es aus dem Auswerten des XPath-Ausdruckes im select-Attribut. XPath kann also nicht nur Dinge adressieren sondern auch komplizierteres anstellen und nicht nur XML-Elemente zurückgeben. Leicht übertrieben: das eigentlich spannende an XSLT ist XPath, der Rest ist letztendlich nur nervig zu tippendes XML.

    Wenn man innerhalb eines Templates einen XPath-Ausdruck auswerten will, befindet man sich im Eingabedokument auf der gedanklichen XPath-Adressierungs-Ebene des umgebenden Templates. Wir befinden uns also auf der Pfadebene /exportData/Book. Wenn man nun einen XPath-Ausdruck macht, wird dieser daran gehängt. „field“ zur Adressierung der field-Elemente wäre absolut also /exportData/Book/field.

    Wir wollen aber nicht alle drei field-Elemente auf einmal zurück kriegen, sondern jeweils den Inhalt eines spezifischen field-Elementes. Wenn man ein Element mit XPath adressiert, allerdings nur unter einer bestimmten Bedingung wird diese Bedingung in eckigen Klammern an das Element dran gehängt. Im XPath-Ausdruck field[@name = 'title'] ist also @name = 'title' die Bedingung. Erkennbar durch das Gleichheitszeichen eindeutig ein Vergleich. An den Wert von Attributen kommt man mit XPath-Adressierung mit dem vorangestellten „@“ vor dem Attributnamen heran. Es wird hier der Wert des Attributes „name“ des field-Elementes mit dem String 'title' verglichen und aus der Bedingung heraus entweder ein „Ja“ oder ein „Nein“ zurück gegeben.

    Als Satz also: „Ich möchte das oder die field-Element(e), bei denen der Attributwert von »name« gleich dem String »title« ist.“ Der Inhalt des field-Elementes wird dann von xsl:value-of in Text verwandelt und ins Resultatdokument eingefügt. Praktischerweise ist in Deinem Eingabedokument jeder Attributwert nur einmal vorhanden, ansonsten würde nur das erste field-Element zurück gegeben, auf das der XPath-Ausdruck zutrifft.

    Wendet man das zweite Stylesheet auf Dein Eingabedokument an, kommt diese Ausgabe raus:

    ~~~csv a;b;c
      d;e;f

      
    Tada.  
      
      
    Tim
    
    1. Hallo Tim,
      vielen, vielen Dank für Deine ausführliche Antwort.Funktioniert Prima. Ich habe noch nicht alles komplett verstanden aber ich geb mir Mühe. Ich habe bisher nur mit html und css und javascript zu tun gehabt. Nun reichen mir die statischen Websites nicht mehr und ich taste mich langsam an mehr ran. Da ich von der graphischen Seite komme ist vieles für mich Neuland.
      grüße,
      Anja