如何用XSLT对满足colspan条件的连续ENTRY元素分组并添加属性
XSLT实现表格连续列合并(添加NAMEST/NAMEEND属性)
需求说明
需要为XML表格中满足以下条件的ENTRY元素添加NAMEST和NAMEEND属性:
- 该元素后面跟着连续的一个或多个
CONTENT值为##colspan##的ENTRY元素 NAMEST属性值为合并列的起始列名(当前元素的COLNAME)NAMEEND属性值为合并列的结束列名(连续##colspan##元素的最后一个的COLNAME)- 同时移除所有
CONTENT值为##colspan##的ENTRY元素
当前XML表格
<TABLE> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>42</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>155</CONTENT> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT></CONTENT> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="5" COLNAME="col5"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="6" COLNAME="col6"> <CONTENT>##colspan##</CONTENT> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>147</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>jalla</CONTENT> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> <ENTRY COLNUM="6" COLNAME="col6"> <CONTENT>##colspan##</CONTENT> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>147</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>300</CONTENT> </ENTRY> <ENTRY COLNUM="5" COLNAME="col5"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="6" COLNAME="col6"> <CONTENT>##colspan##</CONTENT> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>1814</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>1905</CONTENT> </ENTRY> <ENTRY COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> <ENTRY COLNUM="6" COLNAME="col6"> <CONTENT>##colspan##</CONTENT> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT/> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> <ENTRY COLNUM="6" COLNAME="col6"> <CONTENT>##colspan##</CONTENT> </ENTRY> </ROW> </TABLE>
期望输出XML
<TABLE DEFINITION="false"> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>42</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>155</CONTENT> </ENTRY> <ENTRY NAMEST="col3" NAMEEND="col6" COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>147</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT>155</CONTENT> </ENTRY> <ENTRY NAMEST="col3" NAMEEND="col4" COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY NAMEST="col5" NAMEEND="col6" COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> </ROW> <ROW> <ENTRY NAMEST="col1" NAMEEND="col2" COLNUM="1" COLNAME="col1"> <CONTENT>147</CONTENT> </ENTRY> <ENTRY COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY NAMEST="col4" NAMEEND="col6" COLNUM="4" COLNAME="col4"> <CONTENT>300</CONTENT> </ENTRY> </ROW> <ROW> <ENTRY NAMEST="col1" NAMEEND="col3" COLNUM="1" COLNAME="col1"> <CONTENT>1814</CONTENT> </ENTRY> <ENTRY COLNUM="4" COLNAME="col4"> <CONTENT>1905</CONTENT> </ENTRY> <ENTRY NAMEST="col5" NAMEEND="col6" COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> </ROW> <ROW> <ENTRY COLNUM="1" COLNAME="col1"> <CONTENT>##colspan##</CONTENT> </ENTRY> <ENTRY COLNUM="2" COLNAME="col2"> <CONTENT/> </ENTRY> <ENTRY NAMEST="col3" NAMEEND="col4" COLNUM="3" COLNAME="col3"> <CONTENT/> </ENTRY> <ENTRY NAMEST="col5" NAMEEND="col6" COLNUM="5" COLNAME="col5"> <CONTENT/> </ENTRY> </ROW> </TABLE>
尝试的XSLT代码(存在问题)
<xsl:variable name="colspanText" as="xs:string" select="'##colspan##'"/> <xsl:template match="ENTRY"> <xsl:variable name="myColName" as="xs:string" select="@COLNAME"/> <!-- Keep all ENTRY elements except those with ##colspan## in CONTENT. Element #1 should always be kept though. --> <xsl:if test="not(CONTENT = $colspanText and not(position() = 2))"> <xsl:copy> <!-- Add NAMEST and NAMEEND parameters if we have a colspan --> <xsl:for-each-group select="following-sibling::ENTRY" group-adjacent="CONTENT = $colspanText"> <xsl:if test="current-grouping-key()"> <xsl:attribute name="NAMEST" select="$myColName"/> <xsl:attribute name="NAMEEND" select="@COLNAME" /> </xsl:if> </xsl:for-each-group> <!-- Copy everything else --> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:if> </xsl:template>
解决方案(正确的XSLT代码)
使用XSLT 2.0+的分组功能,按ROW分组后,对每个ROW内的ENTRY以非##colspan##的元素为分组起始,将后续连续的##colspan##元素归为同一组,再为起始元素添加合并属性:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:variable name="colspanText" as="xs:string" select="'##colspan##'"/> <!-- 复制所有节点和属性,除非有专门的模板匹配 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 处理TABLE元素,添加DEFINITION属性 --> <xsl:template match="TABLE"> <TABLE DEFINITION="false"> <xsl:apply-templates/> </TABLE> </xsl:template> <!-- 处理ROW元素,对ENTRY进行分组处理 --> <xsl:template match="ROW"> <ROW> <!-- 分组规则:以非##colspan##的ENTRY为起始,后续连续的##colspan##元素归为同一组 --> <xsl:for-each-group select="ENTRY" group-starting-with="ENTRY[not(CONTENT = $colspanText)]"> <!-- 复制起始的ENTRY元素 --> <xsl:copy> <!-- 如果组内除了起始元素还有其他元素(即存在连续的##colspan##),添加合并属性 --> <xsl:if test="count(current-group()) > 1"> <xsl:attribute name="NAMEST" select="@COLNAME"/> <xsl:attribute name="NAMEEND" select="current-group()[last()]/@COLNAME"/> </xsl:if> <!-- 复制原有的属性和子节点 --> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:for-each-group> <!-- 单独处理那些本身是##colspan##的ENTRY(不在任何起始组里的) --> <xsl:apply-templates select="ENTRY[CONTENT = $colspanText][not(preceding-sibling::ENTRY[1][not(CONTENT = $colspanText)])]"/> </ROW> </xsl:template> <!-- 忽略所有##colspan##的ENTRY,除非是单独存在的(上面的模板已经处理) --> <xsl:template match="ENTRY[CONTENT = $colspanText]"/> </xsl:stylesheet>
代码说明
- 基础复制模板:默认复制所有节点和属性,保证非目标元素的结构不变。
- TABLE模板:为TABLE元素添加
DEFINITION="false"属性,匹配期望输出。 - ROW模板:
- 使用
group-starting-with将ENTRY分组,每个组以非##colspan##的元素开头,后续连续的##colspan##元素归入同一组。 - 对每个组的起始元素,若组内有多个元素,添加
NAMEST和NAMEEND属性,分别取起始元素和组内最后一个元素的COLNAME。 - 单独处理那些本身是
##colspan##且前面没有非##colspan##元素的ENTRY(如第五行的col1),保留其原结构。
- 使用
- 忽略模板:默认忽略所有
CONTENT为##colspan##的ENTRY,避免重复输出。
内容的提问来源于stack exchange,提问作者Hermund
相关产品推荐
相关产品推荐

