2009-06-24 2 views
2

Lassen Sie uns sagen, ich habe eine leere XML-Datei wie folgt:Möglich, xsl-Transformation auf dynamisch generierten XML zu tun?

<root></root> 

Und ich möchte ein Element hinzufügen während einer XSL-Transformation wie so zu verankern:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" /> 

    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/"> 
    <xsl:copy> 
     <xsl:element name="root"> 
     <xsl:element name="label">Some text</xsl:element> 
     </xsl:element> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Umwandlung der XML-Datei gibt mir:

Allerdings möchte ich die XSL-Datei ändern, so dass ich die Runtime-generierte XML transformieren kann. Grundsätzlich möchte ich, dass das XSL denkt, dass die ursprüngliche XML-Datei das dynamisch erstellte XML enthält und anschließend eine HTML-Transformation durchführt.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes" /> 

    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/"> 
    <xsl:copy> 
     <xsl:element name="root"> 
     <xsl:element name="label">Some text</xsl:element> 
     </xsl:element> 
    </xsl:copy> 
    </xsl:template> 

    <!-- This part is new --> 
    <xsl:template match="//label"> 
    <b> 
     <xsl:value-of select="." /> 
    </b> 
    </xsl:template> 

</xsl:stylesheet> 

In diesem Fall wäre meine gewünschte Ausgabe: <b>label</b>.

Ist es möglich, dies in einer XSL-Datei zu tun? Betrachte ich dieses Problem falsch?

Antwort

0

Antwort Dougs ist technisch korrekt, und er hat mich auf dem richtigen Weg der Suche nach der nodeset Erweiterungsfunktion (danke!). Der einzige Unterschied ist, dass ich den Xalan-java Prozessor anstelle von Microsoft verwende. Hier ist eine Arbeits (sehr einfach) Beispiel dafür, was ich versuche, achive:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:xalan="http://xml.apache.org/xalan" exclude-result-prefixes="xalan"> 
<xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes"/> 

<xsl:template match="label"> 
    <b><xsl:value-of select="." /></b> 
</xsl:template> 

<xsl:template name="someTemplate"> 
    <root> 
     <label>hey there</label> 
    </root> 
</xsl:template> 

<xsl:template match="/"> 
    <xsl:variable name="gen"> 
     <xsl:call-template name="someTemplate" /> 
    </xsl:variable> 

    <xsl:apply-templates select="xalan:nodeset($gen)//label" /> 
</xsl:template> 

</xsl:stylesheet> 

Die Grundidee ist, dass Sie eine Variable erstellen (hier genannt „gen“), die eine Vorlage nennt („someTemplate“), das erstellt einige XML. Dieser XML-Baum wird dann von der Funktion xalan: nodeset verarbeitet. Hier führe ich Apply-Templates aus und bearbeite alle Label-Felder, die mit meiner Label-Vorlage übereinstimmen und das fett gedruckte Label erstellen.

Der Grund, warum ich das alles mache, ist, weil ich an einer Web-App arbeite, wo der gesamte Inhalt auf jeder Seite in XML definiert und dann mit XSL verarbeitet wird. Das funktioniert großartig, bis Sie Teile der Seite mit den Ergebnissen eines AJAX-Aufrufs füllen wollen, denn dann müssen Sie entweder das neue HTML innerhalb einer JavaScript-Funktion aus dem zurückgegebenen XML (unordentlich und fehlerhaft) erstellen oder die AJAX-XML-Antwort weiterleiten eine XSL-Transformation auf der Serverseite und fügen Sie einfach das xhtml-Ergebnis in die Seite ein.

In meiner Situation, weil ich jede Webseite in XML beschreibe, habe ich einfache, generische Elemente erstellt, die "Typen" haben. Meine Elemente sind "Block", das ist ein beliebiger Inhaltsblock. Ein Block kann Unterknoten wie "Schaltfläche" (die eine Verknüpfung oder Schaltfläche ist), "Liste" (die mehrere Blöcke ist), "Bild" oder "Eingabe" haben. Die Details des Schemas sind irrelevant; Ich wollte nur zeigen, dass mein XML-Schema sehr einfach und generisch ist.

Auf der anderen Seite wird die AJAX XML-Antwort, die ich zum Aktualisieren/Aufbau der Seite abrufe, von einem Drittanbieter erstellt, der mein Schema nicht verwendet. Im Gegenteil, jeder Service, den sie bereitstellen, gibt für jede eindeutige Anfrage ein sehr spezifisches XML zurück. Aus diesem Grund brauche ich eine Möglichkeit, um ihre sehr spezifische XML in meine sehr generische XML zu konvertieren, so dass ich sie mit einem einzigen Satz von XSL-Vorlagen transformieren kann.

Hier ist ein Beispiel für eine der xsl-Vorlagen, die ich jetzt verwende, die die AJAX-Antwort transformiert und ihre spezifische XML in meine generische XML konvertiert.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:xalan="http://xml.apache.org/xalan" exclude-result-prefixes="xalan"> 
<xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes"/> 

<!-- base.xsl includes a generic template that matches all blocks and routes 
     to the needed template based on the block type --> 
<xsl:include href="base.xsl" /> 

<xsl:template name="templateForAJAXResponse"> 
    <block> 
     <type>list</type> 
     <list> 
      <type>itemBrowser</type> 
      <xsl:for-each select="/response/items/item"> 
       <block> 
        <type>button</type> 
        <button> 
         <type>imageButton</type> 
         <href>something.action</href> 
         <src><xsl:value-of select="src" /></src> 
         <title><xsl:value-of select="name" /></title> 
        </button> 
       </block> 
      </xsl:for-each> 
     </list> 
    </block> 
</xsl:template> 

<xsl:template match="/"> 
    <xsl:variable name="gen"> 
     <xsl:call-template name="templateForAJAXResponse" /> 
    </xsl:variable> 

    <div> 
     <xsl:apply-templates select="xalan:nodeset($gen)/block" /> 
    </div> 
</xsl:template> 

</xsl:stylesheet> 
3

Es gibt drei wesentliche Elemente zum Ausführen einer Multi-Pass-Transformation.

  • <xsl:import>
  • <xsl:apply-imports>
  • node-set() Erweiterungsfunktion

Das Beispiel verläuft ein XML-Dokument durch zwei Transformationen seriell. Das heißt, es übergibt den Inhalt durch eine Transformation, die Namespace-Knoten entfernt und dann die Ausgabe der ersten Transformation übernimmt und diese durch eine zweite Transformation weiterleitet, die den Titel des Dokuments ändert. In diesem Fall ist das Dokument ein XHTML-Dokument. Die zweite Transformation ist so geschrieben, dass sie ein XHTML-Dokument mit einem definierten Namespace nicht akzeptieren kann.

Original-XHTML-Dokument

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
    <title>This is the old title</title> 
    </head> 
    <body> 
    <p>This is some text.</p> 
    </body> 
</html> 

Nach dem Durchlauf 1 (Entfernen Namensraum-Knoten)

<html> 
    <head> 
    <title>This is the old title</title> 
    </head> 
    <body> 
    <p>This is some text.</p> 
    </body> 
</html> 

Ergebnis (nach Pass 2)

Beachten Sie, dass der Titeltext geändert hat.

<html> 
    <head> 
    <title>This is the new title</title> 
    </head> 
    <body> 
    <p>This is some text.</p> 
    </body> 
</html> 

XSLT für Pass 1

Diese Transformation gilt Vorlagen in dieser Datei auf den Inhalt Namensraum-Knoten, sondern kopiert den Rest des Inhalts zu entfernen. Wenden Sie dann während des zweiten Durchgangs die Vorlagen an, die in der importierten XSLT mithilfe des Tags <xsl:apply-imports> definiert sind. Die Vorlagen für Pass 2 werden mit dem Tag <xsl:import> importiert. Die Ergebnisse des ersten Durchgangs werden in einer Variablen namens "treefrag" gespeichert. Das Baumfragment wird mit der Erweiterungsfunktion "node-set()" in einen Knotensatz konvertiert. In diesem Beispiel wird der Microsoft XML-Parser 4.0 verwendet, daher wird der Namespace urn:schemas-microsoft-com:xslt deklariert.

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
> 

    <xsl:import href="pass2.xslt" /> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" /> 

    <xsl:template match="/"> 
    <xsl:param name="pass">1</xsl:param> 

    <xsl:choose> 
     <xsl:when test="$pass=1"> 
     <xsl:variable name="treefrag"> 
      <xsl:apply-templates> 
      <xsl:with-param name="pass" select="$pass" /> 
      </xsl:apply-templates> 
     </xsl:variable> 
     <xsl:variable name="doc" select=" 
      msxsl:node-set($treefrag) 
     " /> 
     <xsl:apply-templates select="$doc"> 
      <xsl:with-param name="pass">2</xsl:with-param> 
     </xsl:apply-templates> 
     </xsl:when> 
     <xsl:when test="$pass=2"> 
     <xsl:apply-imports /> 
     </xsl:when> 
    </xsl:choose> 

    </xsl:template> 

    <!-- identity template without namespace nodes --> 
    <xsl:template match="*"> 
    <xsl:param name="pass">2</xsl:param> 

    <xsl:choose> 
     <xsl:when test="$pass=1"> 
     <xsl:element name="{name()}"> 
      <xsl:apply-templates select="@*|node()"> 
      <xsl:with-param name="pass" select="$pass" /> 
      </xsl:apply-templates> 
     </xsl:element> 
     </xsl:when> 
     <xsl:when test="$pass=2"> 
     <xsl:apply-imports /> 
     </xsl:when> 
    </xsl:choose> 
    </xsl:template> 

    <xsl:template match="@*|text()|comment()|processing-instruction()"> 
    <xsl:param name="pass">2</xsl:param> 

    <xsl:choose> 
     <xsl:when test="$pass=1"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"> 
      <xsl:with-param name="pass" select="$pass" /> 
      </xsl:apply-templates> 
     </xsl:copy> 
     </xsl:when> 
     <xsl:when test="$pass=2"> 
     <xsl:apply-imports /> 
     </xsl:when> 
    </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

XSLT für Pass 2

Diese Transformation ändert einfach den Inhalt des Titel-Tag können Kopien der ganze Rest des Inhalts.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

    <xsl:template match="title"> 
    <title>This is the new title</title> 
    </xsl:template> 

    <!-- identity template --> 
    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Quelle Artikel: "XSLT: Multi-pass transforms"

+0

Danke für die Antwort, Doug. Dies brachte mich auf den richtigen Weg und ich konnte eine Lösung für mein tatsächliches Problem finden, detailliert in meiner eigenen Antwort, die ich gepostet habe. –