Pregunta

Al ser relativamente nuevo en XSLT Tengo lo que espero sea una simple pregunta. Tengo algunos archivos XML planas, que pueden ser bastante grandes (por ejemplo. 7MB) que tengo que hacer 'más jerárquica'. Por ejemplo, el XML plana podría tener este aspecto:

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

y debe terminar pareciéndose a esto:

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

Tengo un XSLT de trabajo para esto, y que, esencialmente, consigue un conjunto de nodos de todos los elementos B y luego utiliza la 'siguiente-hermano' eje para obtener un conjunto de nodos de los nodos siguiente nodo actual b (es decir. Following-sibling :: * [position () = $ nodePos]). A continuación, la recursividad se utiliza para agregar los hermanos en el árbol de resultados hasta que se encuentre otro elemento B (He parametrizado, por supuesto, para que sea más genérico).

I también tiene una solución que simplemente envía la posición en el XML de la siguiente b nodo y selecciona los nodos después de que uno después del otro (usando la recursión) a través de una selección * [position () = $ nodePos].

El problema es que el tiempo para ejecutar los aumentos de transformación inaceptablemente con el tamaño del archivo XML. Buscando en ella con XML Spy parece que es la 'posición () =' 'siguiente-hermano' y que se toma el tiempo en los dos métodos respectivos.

Lo que realmente necesita es una manera de limitar el número de nodos en las selecciones anteriores, se llevan a cabo por lo menos comparaciones: cada vez que se pone a prueba la posición, todos los nodos del conjunto de nodos se prueba para ver si su posición es la correcta . Hay una manera de hacer eso ? ¿Alguna otra sugerencia?

Gracias,

Mike

¿Fue útil?

Solución

Yes there is a way to do it much more efficiently: See Muenchian grouping. If having looked at this you need more help with the details, let us know. The key you'll need is something like:

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

Then you can iterate over the <b> elements, and for each one, use key('elements-by-group', generate-id()) to get the elements that immediately follow that <b>.

The task of "making the XML more hierarchical" is sometimes called up-conversion, and your scenario is a classic case for it. As you may know, XSLT 2.0 has very useful grouping features that are easier to use than the Muenchian method.

In your case it sounds like you would use <xsl:for-each-group group-starting-with="b" /> or, to parameterize the element name, <xsl:for-each-group group-starting-with="*[local-name() = 'b']" />. But maybe you already considered that and can't use XSLT 2.0 in your environment.

Update:

In response to the request for parameterization, here's a way to do it without a key. Note though that it may be much slower, depending on your XSLT processor.

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

As noted in the comment, you can keep the performance benefit of keys by defining several different keys, one for each possible value of the parameter. You then select which key to use by using an <xsl:choose>.

Update 2:

To make the group-starting element be defined based on /*/*[2], instead of based on a parameter, use

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

Otros consejos

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

This is the fine grained trasversal pattern:

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

Output:

<D0011>
    <b>
        <c></c>
        <d></d>
        <e></e>
    </b>
    <b>
    ....
    ....
    </b>
</D0011>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top