2017-05-09 1 views
0

Je suis un programmeur POO assez fort, donc j'ai un peu de mal à comprendre comment XSLT "pense" comme un langage fonctionnel.(XSLT) Comment parcourir récursivement la même liste pour chaque élément de la liste?

Les données réelles que je travaille avec est sensible, donc au lieu supposons que j'ai une liste XML de <albums> qui contiennent un <artist>, <songs> et chaque chanson peut ou non avoir un <guest_artist>.

Environ quelque chose comme ceci:

<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> 

Mon but est de produire un fichier texte CSV de toutes les <guest_artist> qui sont également l'artiste principal sur une autre <album>, et l'album dans lequel ils apparaissent en tant qu'invité .

La sortie devrait ressembler à ceci:

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

Mon approche initiale était de <for-each> sur chaque /album/guest_artist. Tout d'abord, stockez le nom de l'artiste invité, puis à l'intérieur de cette boucle, <for-each> à nouveau sur chaque ../album/artist_name et voir si la variable enregistrée correspond à l'un des noms d'artistes. Dans la boucle interne, s'il y avait une correspondance, j'écris une ligne.

à peu près comme ceci:

<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> 

(je ne suis pas préoccupé par les doublons, mais si je peux les bases sur, je peux résoudre ce problème moi-même..)

Ce produit étrange résultats. Il semble y avoir une liste de tous les artistes invités d'abord, puis une virgule, puis tous les noms d'album, puis un saut de ligne.

Je ne demande pas le code (pseudo-code, peut-être). Au contraire, je ne comprends tout simplement pas les fonctionnalités de XSLT que je dois examiner, afin que cela se produise. Il semble que chaque boucle ne se comporte pas comme je le souhaite. Il n'est également pas clair comment la portée est gérée. Je soupçonne que <templates> sera utile, mais j'ai du mal à comprendre ce qu'ils font et comment.

J'ai suivi le cours W3 School et quelques autres tutoriels sur XSL, mais ils ne semblent pas couvrir ces spécificités.

Des suggestions?

Répondre

0

La réponse s'est avérée être beaucoup plus simple que prévu. Le principal problème est le "contexte" de chaque boucle. Le premier <for-each> est en train d'évaluer un nœud, mais le second <for-each> imbriqué commence à évaluer un autre nœud. Par conséquent, je dois être en mesure de dire à l'intérieur <for-each> quel noeud est évalué par le <for-each> externe. C'est aussi simple que de l'enregistrer dans une variable. (Oui, dans d'autres endroits, cela s'appelle "scope", mais je ne suis pas certain que "scope" soit le bon terme dans ce contexte).

Il est sorti à peu près comme ceci:

<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> 

Mes chemins pourraient être hors de cet exemple. La traduction de données réelles en scénario d'exemple n'est pas 100%, surtout quand je suis encore nouveau sur XSL. Mais, la version de production fonctionne ;-)

+0

Ce code semble très procédural en apparence.Les utilisateurs XSLT les plus expérimentés utiliseraient des règles de modèle plutôt que des boucles for-each ici. Et le 'xsl: if' directement dans' xsl: for-each' devrait être un prédicat: '

+0

Incidemment, j'ai utilisé le terme "for-each loops" ici, que Martin critique dans sa réponse supprimée. Il a raison: "loop" est une conversation lâche. Correctement, c'est un opérateur de mappage fonctionnel qui mappe une séquence d'entrée à une séquence de sortie. La principale différence est que l'ordre d'évaluation d'une "boucle" est défini, alors qu'un opérateur de mappage peut traiter les éléments de la séquence d'entrée dans n'importe quel ordre, ou en parallèle. –

+0

C'est une information utile. L'un des problèmes que j'essaie de résoudre consiste à traiter une quantité massive de données plus rapidement. Il est probable qu'un utilisateur XSL plus expérimenté sache déjà que cette approche est fondamentalement paralysée. – Matt