2009-03-19 16 views
3

Je suis en train d'analyser un énorme fichier de mots avec des descriptions de test, et j'ai un problème de portée de nœuds. Word crée essentiellement une liste de paragraphes et je veux les regrouper dans un nœud parent. Donc, pour chaque nœud 'A', je veux regrouper tous les nœuds suivants jusqu'au nœud suivant 'A' en 'A'.Groupe Liste des nœuds dans l'arborescence des nœuds avec XSL

Comment cela peut-il être fait avec XSL?

Exemple: Je l'ai appris à:

<A/> 
<ab/> 
<ac/> 
<A/> 
<ab/> 
<ac/> 

Mais besoin:

<A> 
<ab/> 
<ac/> 
</A> 
<A> 
<ab/> 
<ac/> 
</A> 

Merci!

+0

Vous devez être plus précis, ce qui ne fait spéciale? – Diadistis

+0

@Hugo Ceci est une solution au problème décrit, et il produit le résultat souhaité. Dans le cas où vous avez un problème différent, s'il vous plaît, postez-le afin qu'il puisse être résolu. Vous ne devriez pas avoir de problème pour appliquer cette solution au problème actuel - cela ne fait que produire le résultat souhaité –

Répondre

3

Si vous voulez dire pour correspondre à tous les nœuds suivants <A>, mais viennent avant la prochaine <A>, je pense que vous pouvez utiliser quelque chose comme ceci:

<xsl:template match="A"> 
    <xsl:copy> 
    <!-- start of range --> 
    <xsl:variable name="start" select="count(preceding-sibling::*) + 1" /> 
    <!-- end of range --> 
    <xsl:variable name="stop"> 
     <xsl:choose> 
     <!-- either just before the next A node --> 
     <xsl:when test="following-sibling::A"> 
      <xsl:value-of select="count(following-sibling::A[1]/preceding-sibling::*) + 1" /> 
     </xsl:when> 
     <!-- or all the rest --> 
     <xsl:otherwise> 
      <xsl:value-of select="count(../*) + 1" /> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:variable> 

    <!-- this for debugging only --> 
    <xsl:attribute name="range"> 
     <xsl:value-of select="concat($start + 1, '-', $stop - 1)" /> 
    </xsl:attribute> 

    <!-- copy all nodes in the calculated range --> 
    <xsl:for-each select="../*[position() &gt; $start and position() &lt; $stop]"> 
     <xsl:copy-of select="." /> 
    </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

Pour votre entrée:

<root> 
    <A /> 
    <ab /> 
    <ac /> 
    <A /> 
    <ab /> 
    <ac /> 
</root> 

Je reçois (j'ai laissé l'attribut "range" pour rendre les calculs visibles):

<A range="2-3"> 
    <ab /> 
    <ac /> 
</A> 
<A range="5-6"> 
    <ab /> 
    <ac /> 
</A> 
+0

Il y a probablement une meilleure façon de le faire. Je suis assez curieux des solutions que les autres trouvent. – Tomalak

+0

L'utilisation de clés est généralement plus efficace et offre des solutions plus compactes. Vois ma réponse. –

+0

J'ai fini par utiliser une variante à deux phases de cette réponse. En utilisant l'axe précédent-frère, chaque noeud enfant recevrait un attribut, "appartient-à", et ensuite je les fusionnerais dans une seconde étape. Merci! – Hugo

4

Il existe une solution simple et très puissante utilisant des clés.

Cette transformation:

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

<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:key name="kFollowing" match="*[not(self::A)]" 
    use="generate-id(preceding-sibling::A[1])"/> 

    <xsl:template match="/*"> 
    <t> 
     <xsl:apply-templates select="A"/> 
    </t> 
    </xsl:template> 

    <xsl:template match="A"> 
    <A> 
     <xsl:copy-of select= 
      "key('kFollowing',generate-id())"/> 
    </A> 
    </xsl:template> 
</xsl:stylesheet> 

lorsqu'il est appliqué sur le document XML d'origine:

produit le résultat recherché:

<t> 
    <A> 
     <ab/> 
     <ac/> 
    </A> 
    <A> 
     <ab/> 
     <ac/> 
    </A> 
</t> 

Ne note comment la définition du <xsl:key>, combinée avec l'utilisation de la fonction key() rend la collecte la plus facile et naturelle de tous les éléments frères entre deux éléments <A/> voisins.

+0

Merci pour la réponse. Désolé de ne pas avoir répondu plus tôt, a dû le mettre en attente. C'était la solution la plus élégante, mais je ne pouvais presque que le faire fonctionner. L'As contiendrait tous les ab/ac du nœud courant et vers l'avant. Merci! – Hugo

+0

@Hugo Ceci est une solution au problème décrit, et il produit le résultat souhaité. Dans le cas où vous avez un problème différent, s'il vous plaît, postez-le afin qu'il puisse être résolu. Vous ne devriez pas avoir de problème pour appliquer cette solution au problème actuel - cela produit simplement le résultat voulu. –

3

solution XSLT 2.0:

<xsl:for-each-group select="*" group-starting-with="A"> 
    <xsl:element name="{name(current-group()[1])}"> 
    <xsl:copy-of select="current-group()[position() gt 1]"/> 
    </xsl:element> 
</xsl:for-each-group> 
+0

Très jolie! Impossible d'utiliser t à cause de Ant. Mais merci, a eu une upvote. – Hugo

+0

Excellent. Et bien sûr, on peut utiliser cela avec Ant: http://stackoverflow.com/questions/919692/how-to-execute-xslt-2-0-with-ant –

Questions connexes