Pregunta

A medida similar a esta pregunta (hay más entradas relacionadas, sin embargo, como un nuevo usuario sólo puedo publicar una URL): Xpath Get elementos que son entre 2 elementos

Tengo una pregunta respecto a la selección de un conjunto de elementos que se producen entre otras '/' que delimitan elementos. Esta situación se produce cuando se trata de transformar una tabla HTML plano para una estructura jerárquica de XML mediante XSLT. He intentado utilizar la recursividad en las plantillas, pero Saxon se negó a aceptar esto, ya que resultó en un callejón sin bloqueo, muy probablemente mi culpa, pero vamos a empezar por el principio.

En primer lugar los datos de origen es la tabla HTML:

<table >
    <thead>
        <tr>
            <th>Column 1</th>
            <th>Column 2</th>
            <th>Column 3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th colspan="3" >Group 1</th>
        </tr>
        <tr>
            <td>attribute 1.1.1</td>
            <td>attribute 1.1.3</td>
            <td>attribute 1.1.2</td>
        </tr>
        <tr>
            <td>attribute 1.2.1</td>
            <td>attribute 1.2.2</td>
            <td>attribute 1.2.3</td>
        </tr>
        <tr>
            <td>attribute 1.3.1</td>
            <td>attribute 1.3.2</td>
            <td>attribute 1.3.3</td>
        </tr>
        <tr>
            <th colspan="3" >Group 2</th>
        </tr>
        <tr>
            <td>attribute 2.1.1</td>
            <td>attribute 2.1.3</td>
            <td>attribute 2.1.2</td>
        </tr>
        <tr>
            <td>attribute 2.2.1</td>
            <td>attribute 2.2.2</td>
            <td>attribute 2.2.3</td>
        </tr>
        <tr>
            <td>attribute 2.3.1</td>
            <td>attribute 2.3.2</td>
            <td>attribute 2.3.3</td>
        </tr>
    </tbody>
</table>

La producción prevista en XML sería:

 <groups>
    <group name="Group 1">
        <item attribute1="attribute 1.1.1" attribute2="attribute 1.1.3" attribute3="attribute 1.1.2"/>
        <item attribute1="attribute 1.2.1" attribute2="attribute 1.2.2" attribute3="attribute 1.2.3"/>
        <item attribute1="attribute 1.3.1" attribute2="attribute 1.3.2" attribute3="attribute 1.3.3"/>
    </group>
    <group name="Group 2">
        <item attribute1="attribute 2.1.1" attribute2="attribute 2.1.3" attribute3="attribute 2.1.2"/>
        <item attribute1="attribute 2.2.1" attribute2="attribute 2.2.2" attribute3="attribute 2.2.3"/>
        <item attribute1="attribute 2.3.1" attribute2="attribute 2.3.2" attribute3="attribute 2.3.3"/>
    </group>
</groups>

Así que quieren tener todos los registros de artículos, (elementos TR) y añadirlos a un grupo. Esto básicamente se reduce a seleccionar todos los elementos TR-hermanos siguiente hasta que nos encontramos con uno que tiene un elemento TH como un niño. Si tan sólo pudiera determinar la posición de este primer TR que tiene un niño TH, lo que indica un nuevo título para un grupo, esto se podría hacer con:

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:for-each select="following-sibling::tr[position() < $positionOfNextThElement]">            
                <xsl:call-template name="item"/>
            </xsl:for-each>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

Sin embargo, no soy capaz de determinar la posición de la primera etiqueta TR / TH encontrado.

Como se ha dicho he intentado trabajar con la recursividad en las plantillas: llamar siempre a la plantilla "elemento", y en esta plantilla determinar si queremos invocar en el orden del día, también. Creo que el problema está en la invocación de la plantilla desde dentro de la plantilla. El elemento de contexto no aumenta? Debería entregar un parámetro para determinar lo que el tema que estamos trabajando?

De todos modos, esto fue lo que ocurrió:

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:call-template name="item"/>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

<xsl:template name="item">
    <xsl:element name="item">
        <xsl:attribute name="attribute1"><xsl:value-of select="following-sibling::tr[1]/td[1]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[2]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[3]"/></xsl:attribute>
    </xsl:element>
    <!-- When the next element has not got a TH tag, continue with invoking this template -->
    <xsl:if test="count(following-sibling::tr[1]/th) != 1">
        <xsl:call-template name="item"/>
    </xsl:if>
</xsl:template>

¿Alguna sugerencia sobre cómo realizar esto son bienvenidos!

¿Fue útil?

Solución

Una solución alternativa, equipado para el atributo variable de cuenta sin recursión.

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

  <xsl:template match="table">
    <groups>
      <xsl:apply-templates select="tbody/tr[th]"/>
    </groups>
  </xsl:template>

  <xsl:template match="tr[th]">
    <group name="{th}">
      <xsl:apply-templates select="
        following-sibling::tr[not(th)][
          generate-id(preceding-sibling::tr[th][1]) = generate-id(current())
        ]
      "/>
    </group>
  </xsl:template>

  <xsl:template match="tr">
    <item>
     <xsl:apply-templates select="td" />
    </item>
  </xsl:template>

  <xsl:template match="td">
    <xsl:attribute name="attribute{position()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

Otros consejos

La razón de ese contexto no aumenta cuando se llama a la plantilla "elemento" de forma recursiva es que xs: call-plantilla siempre pasa por el elemento de contexto actual como contexto. Así como se ve entonces, la transformación solo entra en bucle infinito.

Si se asume que siempre se necesita para producir exactamente tres atributos, que ni siquiera es necesario recursividad.

Prueba esto:

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

    <xsl:template match="table">
        <groups>
            <xsl:apply-templates select="tbody/tr[th]"/>
        </groups>
    </xsl:template>

    <xsl:template match="tr[th]">
        <xsl:variable name="id" select="generate-id(.)"/>
        <group name="{string(th)}">
            <xsl:apply-templates
                select="following-sibling::tr[not(th)][generate-id(preceding-sibling::tr[th][1]) = $id]"/>
        </group>
    </xsl:template>

    <xsl:template match="tr">
        <item attribute1="{td[1]}" attribute2="{td[2]}" attribute3="{td[3]}" />                    
    </xsl:template>

</xsl:stylesheet>

Esto funciona mediante la aplicación de plantillas para cada fila de cabecera. Cada uno de los plantilla utiliza tan complicado XPath para llamar "sus" filas siguientes, que son cualquier siguientes filas de hermanos que tienen fila que específica, ya que está primero anterior fila con un encabezado.

Por supuesto, si el número de atributos varían, entonces tendrá que recursivo allí y aumentar pasar un parámetro que indica la posición.

Hay un par de métodos establecidos para la agrupación de XSLT, uno de los cuales es recursivo, al igual que estaba haciendo. Otro método se denomina agrupación Muenchian. Un buen artículo es aquí .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top