Question

Étant relativement nouveau pour XSLT avoir ce que je souhaite est une simple question. J'ai quelques fichiers XML à plat, ce qui peut être assez grand (par exemple. 7MB) que je dois faire « plus hiérarchique ». Par exemple, le XML plat pourrait ressembler à ceci:

<D0011>
    <b/>
    <c/>
    <d/>
    <e/>
    <b/>
    ....
    ....
</D0011>

et il devrait finir par ressembler à ceci:

<D0011>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <b>
 ....
 ....
</D0011>

J'ai un XSLT de travail pour cela, et il obtient essentiellement un ensemble de nœuds de tous les éléments b et utilise ensuite les « following-sibling » axe pour obtenir un ensemble de nœuds des nœuds suivants du noeud courant b (ie. Following-sibling :: * [Position () = $ nodePos]). Ensuite récursion est utilisé pour ajouter les frères et soeurs dans l'arbre résultat jusqu'à ce qu'un autre élément b se trouve (je l'ai paramétrés il bien sûr, pour le rendre plus générique).

J'ai aussi une solution qui envoie simplement la position dans le fichier XML du nœud suivant b et sélectionne les noeuds après que l'un après l'autre (en utilisant récursion) par une sélection * [poste () = $ nodePos].

Le problème est que le temps d'exécuter les augmentations de transformation inacceptable avec la taille du fichier XML. En regardant dans avec XML Spy, il semble que ce soit le « following-sibling » et « la position () = » qui prennent le temps dans les deux méthodes respectives.

Ce que je vraiment besoin est un moyen de limiter le nombre de noeuds dans les sélections ci-dessus, donc moins de comparaisons sont effectuées: chaque fois que la position est testée, chaque nœud du nodeset est testé pour voir si sa position est la bonne . Y-a-t-il un moyen de faire ça ? Toutes les autres suggestions?

Merci,

Mike

Était-ce utile?

La solution

Oui, il y a une façon de le faire beaucoup plus efficacement: Voir Muenchian groupe . Si après avoir regardé ce dont vous avez besoin de plus d'aide avec les détails, laissez-nous savoir. La clé dont vous aurez besoin est quelque chose comme:

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

Ensuite, vous pouvez itérer sur les éléments de <b>, et pour chacun d'eux, l'utilisation key('elements-by-group', generate-id()) pour obtenir les éléments qui suivent immédiatement <b>.

La tâche de « rendre le XML plus hiérarchique » est parfois appelé conversion, et votre scénario est un cas classique pour elle. Comme vous le savez, XSLT 2.0 possède des fonctionnalités de regroupement très utiles qui sont plus faciles à utiliser que la méthode Muenchian.

Dans votre cas, il semble que vous utilisez <xsl:for-each-group group-starting-with="b" /> ou, pour paramétrer le nom de l'élément, <xsl:for-each-group group-starting-with="*[local-name() = 'b']" />. Mais peut-être vous avez déjà considéré que et ne peut pas utiliser XSLT 2.0 dans votre environnement.

Mise à jour:

En réponse à la demande de paramétrage, voici une façon de le faire sans clé. Notez cependant qu'il peut être beaucoup plus lent, en fonction de votre processeur XSLT.

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name() = $sep]">
      <xsl:copy>
         <xsl:copy-of select="following-sibling::*[not(local-name() = $sep)
               and generate-id(preceding-sibling::*[local-name() = $sep][1]) =
                    generate-id(current())]" />
      </xsl:copy>
   </xsl:for-each>      
</xsl:template>

Comme indiqué dans le commentaire, vous pouvez garder le bénéfice de la performance des clés en définissant plusieurs touches différentes, une pour chaque valeur possible du paramètre. Vous sélectionnez ensuite quelle touche utiliser en utilisant un <xsl:choose>.

Mise à jour 2:

Pour définir l'élément de départ de groupe basée sur /*/*[2], au lieu de basé sur un paramètre, l'utilisation

<xsl:key name="elements-by-group"
   match="*[not(local-name(.) = local-name(/*/*[2]))]"
   use="generate-id(preceding-sibling::*
                           [local-name(.) = local-name(/*/*[2])][1])" />

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name(.) = local-name(../*[2])]">
      <xsl:copy>
         <xsl:copy-of select="key('elements-by-group', generate-id())"/>
      </xsl:copy>
   </xsl:for-each>
</xsl:template>

Autres conseils

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

<xsl:template match="D0011">
  <xsl:copy>
    <xsl:apply-templates select="b"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="D0011/b">
  <xsl:copy>
    <xsl:copy-of select="key('k1', generate-id())"/>
  </xsl:copy>
</xsl:template>

Ceci est le modèle de trasversal à grain fin:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="b[1]" name="group">
        <xsl:copy>
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::b[1]" mode="group"/>
    </xsl:template>
    <xsl:template match="b[position()!=1]"/>
    <xsl:template match="b" mode="group">
        <xsl:call-template name="group"/>
    </xsl:template>
</xsl:stylesheet>

Sortie:

<D0011>
    <b>
        <c></c>
        <d></d>
        <e></e>
    </b>
    <b>
    ....
    ....
    </b>
</D0011>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top