XSLT merge rows from adjacent HTML tables, of same structure, in a single table

走远了吗. 提交于 2019-12-13 03:38:35

问题


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.


回答1:


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.




回答2:


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>



回答3:


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>


来源:https://stackoverflow.com/questions/21572625/xslt-merge-rows-from-adjacent-html-tables-of-same-structure-in-a-single-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!