« Die Nebenläufigkeit | Main | JavaScript DOM Object Parser »

XSLT - Exkurs

Weil ichs mal in Erwägung gezogen habe, aber Abstand davon genommen habe, da es eine wirklich nützliche XSLT Lib für JavaScript nocht nicht gibt.
Das Problem ist, dass man XML Daten bekommt (so wie das bei mir der Fall ist), aber diese Daten zum Beispiel als HTML Inseln (siehe letzter Post) in seinen DOM Baum "einhängen" möchte.
Man könnte sich ja jetzt fragen, wieso man dann nicht gleich HTML als Ausgabeformat nutzen möchte, aber letztendlich soll es ja alles schön kompatibel zu anderen Client bleiben und ich denke mal, dass das von mir gewünschte Format alles andere als Standard wäre. Es fehlt also eine Anweisung, um XML in das gewünschte HTML Format zu überführen. Genau das bietet einem XSLT (Extensible Stylesheet Language Transformation).
Hierbei wird durch ein XSL-Script fest gelegt, welche Daten wo im Ausgabeformat (in meinem Fall also HTML) landen sollen.

Also nehmen wir folgendes XML File:

user.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user uri="http://localhost:8080/cotodo/resources/users/3/">
    <email>Test3</email>
    <forename>Test</forename>
    <surname>NeuerTester</surname>
    <userId>3</userId>
    <created uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/1/">
            <taskId>1</taskId>
        </taskRef>
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>
    </created>
    <groups uri="http://localhost:8080/cotodo/resources/users/3/groups/">
        <groupRef uri="http://localhost:8080/cotodo/resources/users/3/groups/1/">
            <groupId>1</groupId>
        </groupRef>
        <groupRef uri="http://localhost:8080/cotodo/resources/users/3/groups/2/">
            <groupId>2</groupId>
        </groupRef>
    </groups>
    <inbox uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>
    </inbox>
    <outbox uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>       
    </outbox>
</user>

Wie man gut erkennt, eine - recht tief - verschachtelte Ansammlung von Datenblöcken. Wichtig für REST sind zum Beispiel auch die Referenzen auf die verschiedenen Objekte (URIs), um ggf. Objekte vollständig nach zuladen.

Meine gewünschte HTML Ausgabe soll dann so aussehen:

user.html:

<li class="user" id="3" uri="http://localhost:8080/cotodo/resources/users/3/">
    <ul class="attributes">
        <li id="email">Test3</li>
        <li id="forename">Test</li>
        <li id="surname">NeuerTester</li>
    </ul>
    <ul class="groups" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/groups/">
        <li class="groupRef">1</li>
        <li class="groupRef">2</li>
    </ul>
    <ul class="outbox" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/outbox/">
        <li class="taskRef">2</li>
    </ul>
    <ul class="inbox" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/inbox/">
        <li class="taskRef">2</li>
    </ul>
    <ul class="created" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/created/">
        <li class="taskRef">1</li>
        <li class="taskRef">2</li>
    </ul>
</li>

Es handelt sich letztendlich um ein Listenelement (welches dann in einer users Liste landet), welches weitere Unterlisten enthält. Die Anzahl der Referenzen habe ich - zur optimaleren Platzausnutzung und zur Übersichtlichkeit - verringert. Letztendlich reicht mir eine Referenz der Liste und kann dann mit Hilfe der RefIds auf die URIs der einzelnen Elemente zugreifen. Das display:none habe ich nur aus dem Grund eingefügt, dass gewisse Elemente nicht sichtbar sein müssen. Mir reicht, dass sie im DOM hinterlegt sind.

Um also so eine Ausgabe zu erreichen benutze ich folgendes Script:


user.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"/>

    <xsl:template match="/">
        <xsl:element name="li">
            <xsl:attribute name="class">user</xsl:attribute>
            <xsl:attribute name="id"><xsl:value-of select="user/userId" /></xsl:attribute>
            <xsl:attribute name="uri"><xsl:value-of select="user/@uri" /></xsl:attribute>
            <xsl:element name="ul">
                <xsl:attribute name="class">attributes</xsl:attribute>
                    <li id="email"><xsl:value-of select="user/email"/></li>
                    <li id="forename"><xsl:value-of select="user/forename"/></li>
                    <li id="surname"><xsl:value-of select="user/surname"/></li>
        </xsl:element>
        <xsl:element name="ul">
                    <xsl:attribute name="class">groups</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />groups/</xsl:attribute>
                    <xsl:for-each select="user/groups/groupRef">   
                        <li class="groupRef"><xsl:value-of select="groupId"/></li>
                    </xsl:for-each>
                </xsl:element>
                <xsl:element name="ul">
                    <xsl:attribute name="class">outbox</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />outbox/</xsl:attribute>
                    <xsl:for-each select="user/outbox/taskRef">
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>   
                <xsl:element name="ul">
                    <xsl:attribute name="class">inbox</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />inbox/</xsl:attribute>
                    <xsl:for-each select="user/inbox/taskRef">   
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>   
                <xsl:element name="ul">
                    <xsl:attribute name="class">created</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />created/</xsl:attribute>
                    <xsl:for-each select="user/created/taskRef">   
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>           
    </xsl:element>      
    </xsl:template>
</xsl:stylesheet>

Hierbei gelten folgende Regeln: Der Zugriff auf die einzelnen Elemente erfolgt über XPath. Also <xsl:value-of select="user/userId" /> bedeute, dass der Wert aus der Hirachie user/ geholte wird, welche zwischen den Tags userId steht. Möchte man auf ein Attribute zugreifen, so benutzt man das @ Zeichen. Wie z.B. bei <xsl:value-of select="user/@uri" />. Ich habe mich immer für die "Root" URI entschieden, weil die anderen URIs sich per Definitionem davon ableiten lassen.

wollte man das XML Document nun direkt als HTML ausgeben haben, so fügt man in obiges Dokument unter

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

folgendes ein:

<?xml-stylesheet type="text/xsl" href="user.xsl"?>


Hierbei sollte die Datei natürlich unter dem gleichen Pfad erreichbar sein wir die user.xml. (Aus Testzwecken war das bei mir der Fall). Aber natürlich kann man auch jede andere URL angeben. Das Einbinden entspricht ungefähr dem von einer CSS Datei.

Empfehlenswert zu dem Thema ist:

sarissa - eine JS Implementierung von XSLT
Die XSL Tutorials von TopXML
XML/XSL Tutorials von DrWeb
und zu guter Letzt:
Die XML/XSL Tutorials von den W3Schools


Blogged with Flock

TrackBack

TrackBack URL for this entry:
http://www.innoq.com/movabletype/mt-tb.cgi/2970

Comments (1)

Tim:

Cooler Artikel, werde ich mir merken.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About

This page contains a single entry from the blog posted on January 9, 2008 7:04 PM.

The previous post in this blog was Die Nebenläufigkeit.

The next post in this blog is JavaScript DOM Object Parser.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.31