2010-10-04 18 views
3

J'ai la structure XML suivante:Comment faire pour supprimer les doublons en fonction du niveau dans la hiérarchie?

<node name="A"> 
    <node name="B"> 
    <node name="C"/> 
    <node name="D"/> 
    <node name="E"/> 
    </node> 
    <node name="D"/> 
    <node name="E"/> 
</node> 

J'ai besoin pour obtenir tous les nœuds feuilles. J'utilise // node [not (node)] pour les obtenir. Maintenant, j'ai besoin de supprimer les doublons en laissant les éléments qui sont plus profonds dans la hiérarchie. Comment je fais ça?

+0

Bonne question (1). Voir ma réponse pour deux solutions - XSLT 1.0 et XSLT 2.0. :) –

Répondre

1

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:strip-space elements="*"/> 

    <xsl:variable name="vallLeaves" select="//node()[not(node())]"/> 

<xsl:template match="/"> 
$vallLeaves: 
    <xsl:copy-of select="$vallLeaves"/> 

$vallDistinctLeaves:  
    <xsl:for-each select="$vallLeaves"> 
     <xsl:if test= 
     "generate-id() 
     = 
     generate-id($vallLeaves[@name 
           = 
           current()/@name 
           ] 
            [1] 
        ) 
    "> 
     <xsl:copy-of select="."/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

lorsqu'il est appliqué sur le document XML fourni:

<node name="A"> 
    <node name="B"> 
    <node name="C"/> 
    <node name="D"/> 
    <node name="E"/> 
    </node> 
    <node name="D"/> 
    <node name="E"/> 
</node> 

produit le résultat souhaité, correct:

$vallLeaves: 
    <node name="C"/> 
<node name="D"/> 
<node name="E"/> 
<node name="D"/> 
<node name="E"/> 

$vallDistinctLeaves:  
    <node name="C"/> 
<node name="D"/> 
<node name="E"/> 

II. XSLT 2.0 Solution:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:variable name="vallLeaves" select="//node()[not(node())]"/> 
    <xsl:variable name="vallDistinctLeaves" as="element()*"> 
     <xsl:for-each-group select="$vallLeaves" group-by="@name"> 
     <xsl:sequence select="."/> 
     </xsl:for-each-group> 
    </xsl:variable> 

<xsl:template match="/"> 
$vallLeaves: 
    <xsl:sequence select="$vallLeaves"/> 

$vallDistinctLeaves:  
    <xsl:sequence select="$vallDistinctLeaves"/> 
</xsl:template> 
</xsl:stylesheet> 

lorsque cette transformation est appliquée sur le même document XML (ci-dessus), les mêmes résultats corrects sont produits:

$vallLeaves: 
    <node name="C"/> 
<node name="D"/> 
<node name="E"/> 
<node name="D"/> 
<node name="E"/> 

$vallDistinctLeaves:  
    <node name="C"/> 
<node name="D"/> 
<node name="E"/> 
+0

Merci beaucoup pour ça! J'essayais des modèles récursifs, des boucles imbriquées mais votre solution est tellement meilleure et efficace. – Mike

+1

+1 Bonne réponse. Aussi, une ligne XPath 2.0: '// node() [not (node ​​())] [@ nom = distinct-values ​​(// node() [not (node ​​())]/@ nom)] [1] 'ou mieux avec des clés dans XSLT 2.0' key ('kLeafByName', distinct-values ​​(// node() [not (node ​​())]/@ nom)])) [1] ' –