XSLT merge rows from adjacent HTML tables, of same

2019-08-29 10:32发布

I am new to XSLT and I have the requirement of bringing all rows of adjacent HTML tables, having the same structure, in a single table, simply by appending all rows of following tables in the first table encountered:

Input:

<div>
<span class="title">sample</span>
<br/>
    some text node1
    <br/>
    some text node2
<p/>
<table class="class1">
   <tbody>
     <tr>
       <td>L1:</td>
       <td>C1</td>
     </tr>
   </tbody>
 </table>
 <p/>
 <span class="section">Section1</span>
<p/>
<table class="class1">
   <tbody>
     <tr>
       <td>L2:</td>
       <td>C2</td>
     </tr>
   </tbody>
 </table>
 <table class="class1">
   <tbody>
     <tr>
       <td>L3:</td>
       <td>C3</td>
     </tr>
   </tbody>
 </table>
    <table class="class1">
   <tbody>
     <tr>
       <td>L4:</td>
       <td>C4</td>
     </tr>
   </tbody>
</table>
    <span class="section">Section2</span>
<p/>
    <table class="class1">
   <tbody>
     <tr>
       <td>L5:</td>
       <td>C5</td>
     </tr>
   </tbody>
</table>
    <table class="class2">
   <tbody>
     <tr>
       <td>L6:</td>
      <td>C6</td>
     </tr>
  </tbody>
</table>
</div>

The resulting output, should merge adjacent tables with @class="class1".

Output:

<div>
<span class="title">sample</span>
<br/>
    some text node1
    <br/>
    some text node2
<p/>
<table class="class1">
   <tbody>
     <tr>
       <td>L1:</td>
       <td>C1</td>
     </tr>
   </tbody>
 </table>
<p/>
<span class="section">Section1</span>
<p/>
<table class="class1">
   <tbody>
     <tr>
       <td>L2:</td>
       <td>C2</td>
     </tr>
     <tr>
      <td>L3:</td>
       <td>C3</td>
    </tr>
     <tr>
       <td>L4:</td>
      <td>C4</td>
     </tr>
  </tbody>
</table>
    <span class="section">Section1</span>
<p/>
<table class="class1">
   <tbody>
     <tr>
       <td>L5:</td>
       <td>C5</td>
     </tr>
   </tbody>
</table>
    <table class="class2">
  <tbody>
    <tr>
      <td>L6:</td>
      <td>C6</td>
    </tr>
  </tbody>
</table>
</div>

Any ideas how this can be realized? I have been trying to do this using group-adjacent, but I'm not getting the intended result, and I don't know what's missing me.

- Edit:

here is my attempt (NOT WORKING PROPERLY).

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="@*|node()">
<xsl:copy>
 <xsl:apply-templates select="@*|node()"/>
 </xsl:copy>
</xsl:template>

<xsl:template match="table[@class='class1']">
<xsl:choose>
<xsl:when test="not(preceding-sibling::*[1][self::table])">
<xsl:copy>
 <xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
  <xsl:choose>
    <xsl:when test="preceding-sibling::*[1]/@class='class1'" />
    <xsl:when test="following-sibling::*[1]/@class='class1'" >
         <table class="class1">
           <tbody>
           <xsl:apply-templates select="tbody/*"/>
           <xsl:for-each select="following-sibling::*">
            <xsl:if test="self::table[@class='class1']">
              <xsl:apply-templates select="tbody/*"/>
             </xsl:if> 
           </xsl:for-each>
           </tbody>
          </table>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
       <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:otherwise>
 </xsl:choose>

NOTE If possible help me do that both in XSLT1.0, enhancing my attempt, and/or XSLT2.0.

3条回答
冷血范
2楼-- · 2019-08-29 10:40

not to disappoint you this time, there was a small change done:

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

<xsl:output method="xml" indent="yes"/>

<xsl:key name="key" match="table[@class = following::*[1][local-name() = 'table']/@class or @class = preceding-sibling::*[1][local-name() = 'table']/@class]" use="@class"/>

<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="table[not(@class = following::*[1][local-name() = 'table']/@class or @class = preceding-sibling::*[1][local-name() = 'table']/@class)]">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="table[@class = following::*[1][local-name() = 'table']/@class or @class = preceding-sibling::*[1][local-name() = 'table']/@class]"/>

<xsl:template match="table[generate-id() = generate-id(key('key', @class)[1])]">
    <table class="{@class}">
        <xsl:copy-of select="key('key', @class)/tbody"/>
    </table>
</xsl:template>

</xsl:stylesheet>
查看更多
一纸荒年 Trace。
3楼-- · 2019-08-29 10:56

Using XSLT 2.0:

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

<xsl:output indent="yes"/>

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="body | div"><!-- need to list other container elements here -->
  <xsl:copy>
    <xsl:for-each-group select="node() except text()[not(normalize-space())]" group-adjacent="boolean(self::table[@class = 'class1'])">
      <xsl:choose>
        <xsl:when test="current-grouping-key()">
          <table class="{@class}">
            <xsl:apply-templates select="thead"/>
            <tbody>
              <xsl:apply-templates select="current-group()/tbody/tr"/>
            </tbody>
          </table>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

This is a correction of the first version which should ensure that all text nodes containing more than white space are also copied. But it assumes that between those tables you want to group there is solely white space.

查看更多
We Are One
4楼-- · 2019-08-29 10:56

Another version using XSLT 2.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="div">
        <xsl:for-each-group select="*" group-adjacent="boolean(self::table[@class='class1'])">
            <xsl:choose>
                <xsl:when test="current-group()[@class='class1'] and current-group()[2]">
                    <table class="{@class}">
                        <tbody>
                            <xsl:apply-templates select="current-group()/descendant::tr"/>
                        </tbody>
                    </table>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:for-each-group select="current-group()" group-adjacent="boolean(self::table[@class='class2'])">
                        <xsl:choose>
                            <xsl:when test="current-group()[@class='class2'] and current-group()[2]">
                                <table class="{@class}">
                                    <tbody>
                                        <xsl:apply-templates select="current-group()/descendant::tr"/>
                                    </tbody>
                                </table>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:copy-of select="current-group()"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each-group>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>
查看更多
登录 后发表回答