2017-05-09 2 views
0

Ich bin ein ziemlich starker OOP-Programmierer, also habe ich ein wenig Mühe herauszufinden, wie XSLT als funktionale Sprache "denkt".(XSLT) Wie rekursiv durchlaufen Sie die gleiche Liste für jedes Element in der Liste?

Die tatsächliche Daten, die ich mit gerade arbeite ist empfindlich, so stattdessen nehmen wir an, ich eine XML-Liste von <albums> haben, die enthalten ein <artist>, <songs> und jeder Song kann oder nicht <guest_artist> haben.

Etwa so etwas wie diese:

<album> 
    <album_name>First One</album_name> 
    <artist_name>SomeGuy</artist_name> 
    <song> 
     <song_name>Somebody</song_name> 
     <guest_artist>SomebodyElse</guest_artist> 
    </song> 
    ... 
</album> 

Mein Ziel ist es, eine CSV-Textdatei aller <guest_artist>, die auch die primären Künstler auf einer anderen <album>, und das Album, in dem sie erscheinen als Gast zu produzieren .

wie folgt aussehen Die Ausgabe sollte:

Guest Artist Name,Album on which they were a Guest 
SombodyElse,First One 

Mein erster Ansatz war zu <for-each> über jeden /album/guest_artist. Speichern Sie zunächst den Namen des Gastkünstlers und dann innerhalb dieser Schleife erneut <for-each> über jede ../album/artist_name und überprüfen Sie, ob die gespeicherte Variable mit einem der Künstlernamen übereinstimmt. In der inneren Schleife, wenn es eine Übereinstimmung gab, schreibe ich eine Zeile aus.

Etwa so:

<xsl:variable name="linefeed" select="'&#xA;'"/> 
<xsl:template match="/"> 
    <!-- Header Row Begins --> 
    <xsl:textGuest Artist Name,Album on which they were a Guest</xsl:text> 
    <xsl:value-of select="$linefeed"/> 

    <!-- Data Row Begins --> 
    <xsl:for-each select="/album/song/guest_artist"> 
     <xsl:variable name="guest_name" select="guest_artist"/> 
     <xsl:variable name="this_album_name" select="../album_name"/> 
     <xsl:for-each select="../../album"> 
      <xsl:if test="$guest_name=artist_name"> 
       <xsl:value-of select="album/song/guest_artist"/> 
       <xsl:text>,</xsl:text> 
       <xsl:value-of select="$this_album_name"/> 
       <xsl:value-of select="$linefeed"/> 
      </xsl:if> 
     </xsl:for-each> 
    <!-- Data Row End --> 
</xsl:template> 

(Ich bin nicht besorgt über Duplikate, aber wenn ich die Grundlagen herausfinden kann, dann kann ich das Problem selbst lösen..)

Dies erzeugt seltsames Ergebnisse. Es scheint zunächst alle Gastkünstler aufzulisten, dann ein Komma, dann alle Albumnamen, dann einen Zeilenvorschub.

Ich frage nicht nach dem Code (Pseudo-Code, vielleicht). Vielmehr verstehe ich einfach nicht die Funktionen von XSLT, die ich untersuchen muss, um dies zu erreichen. Es scheint wie für-jeder Loops verhalten sich nicht so, wie ich es von ihnen erwarte. Es ist auch nicht klar, wie der Umfang gehandhabt wird. Ich vermute, dass <templates> wird nützlich sein, aber ich habe eine harte Zeit herauszufinden, was sie tun, und wie.

Ich habe den W3-Schulkurs und einige andere Tutorials über XSL durchlaufen, aber sie scheinen diese Besonderheiten nicht zu berücksichtigen.

Irgendwelche Vorschläge?

Antwort

0

Die Antwort erwies sich als viel einfacher als ich erwartet hatte. Das Hauptproblem ist der "Kontext" jeder Schleife. Die erste <for-each> bewertet einen Knoten, aber dann beginnt die zweite, verschachtelte <for-each> mit der Auswertung eines anderen Knotens. Folglich muss ich in der Lage sein, dem inneren <for-each> zu sagen, welcher Knoten von der äußeren <for-each> ausgewertet wird. Das ist so einfach wie das Speichern in einer Variablen. (Ja, an anderen Orten heißt das "Scope", aber ich bin mir nicht sicher, "Scope" ist in diesem Zusammenhang der richtige Begriff).

Es kam ungefähr so ​​aus:

<xsl:variable name="linefeed" select="'&#xA;'"/> 
<xsl:template match="/"> 
    <!-- Header Row Begins --> 
    <xsl:textGuest Artist Name,Album on which they were a Guest</xsl:text> 
    <xsl:value-of select="$linefeed"/> 

    <!-- Data Row Begins --> 
    <xsl:for-each select="/album/guest_artist"> 
---> <xsl:variable name="current_node" select="current()"/> 
     <xsl:variable name="guest_name" select="guest_artist"/> 
     <xsl:variable name="this_album_name" select="../album_name"/> 
     <xsl:for-each select="../../album"> 
      <xsl:if test="$guest_name=artist_name"> 
--->   <xsl:value-of select="$current_node/guest_artist"/> 
       <xsl:text>,</xsl:text> 
--->   <xsl:value-of select="$current_node/../../album_name"/> 
       <xsl:value-of select="$linefeed"/> 
      </xsl:if> 
     </xsl:for-each> 
    <!-- Data Row End --> 
</xsl:template> 

Meine Wege aus diesem Beispiel sein könnte. Die Übersetzung von realen Daten in ein Beispielszenario ist nicht 100%, besonders wenn ich noch neu in XSL bin. Aber die Serienversion funktioniert ;-)

+0

Dieser Code sieht sehr verfahrenstechnisch aus.Die meisten erfahrenen XSLT-Benutzer würden hier Vorlagenregeln anstelle von for-each-Schleifen verwenden. Und das 'xsl: if' direkt in' xsl: for-each' sollte eigentlich ein Prädikat sein: '

+0

Übrigens habe ich hier den Begriff "for-each loops" verwendet, den Martin in seiner gelöschten Antwort kritisiert. Er hat recht: "loop" ist loses Gespräch. Korrekterweise ist es ein funktionaler Mapping-Operator, der eine Eingabesequenz einer Ausgabesequenz zuordnet. Der Hauptunterschied besteht darin, dass die Reihenfolge der Auswertung einer "Schleife" definiert ist, wohingegen ein Abbildungsoperator die Elemente in der Eingabesequenz in beliebiger Reihenfolge oder parallel verarbeiten kann. –

+0

Das sind hilfreiche Informationen. Eines der Probleme, die ich lösen möchte, ist die schnellere Verarbeitung einer großen Datenmenge. Es ist wahrscheinlich, dass ein erfahrenerer XSL-Benutzer bereits wissen würde, dass dieser Ansatz grundlegend beeinträchtigt ist. – Matt

Verwandte Themen