xsl的preceding::跟following::

本文探讨了使用XML和XSL实现MVC架构中视图功能时遇到的问题,特别是在XSL中实现数据分组的难点。文章分析了XSL中for-each排序与节点顺序不一致导致的问题,并提出了两种解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

案例是这样,因为现在架构是采用了mvc,即视图跟数据的提供模块分开,一个模块负责获取数据,而前台负责将数据按所需要的需求显示出来,现在我们是用java的来实现model的功能,即后台的数据操作,而前台的view,是采用xml + xsl来实现。因此,在设计时候的一个理念就是把数据按照一个个的VO在后台java填装好之后,用xml传给客户端,客户端接收到xml的数据的时候,就可以用xsl文件来对数据进行显示。

根据这种想法,当前台需要对数据进行统计,或分组时,我们将这种操作看成是一种特殊的视图显示,我们当然希望能在xsl里面完成。对于统计,xsl提供了sum跟count函数,实际使用没什么太大问题。

问题在于分组上面,xsl虽然提供了对node的分组,但当我们想知道,什么时候是分组的分界线的时候,即何时是分组开始,何时分组结束的时候,xsl并没有提供很好的支持。

一开始,我是用了xsl里面的following::跟preceding::再加上count,来计算之前的节点数跟之后的节点数,其中某个为零的时候,就可以判断此时是分组开始还是分组结束。可是,这时xsl的语法设计得并不好,xsl里面有个很常用的for-each的语句,可以用来进行对节点进行循环显示并对他们进行sorting,问题就出在for-each的sorting上,本来,xsl的这个sorting要给的印象是它已经把整个xml的数据全部重新sort了一遍,即顺序已经排好了。因为在for-each语句里面,它的节点确确实实是照着sort了之后的顺序走的。可是呢,当在for-each里面用了following::或者是preceding::时,就不是这样了,他并不认for-each sort了之后的顺序,而只是认xml里面各个node点排序的顺序。

就会造成这样的一个问题,当在for-each里面sort by两个以上的关键字,而认following和preceding的关键字少于sorting的关键字时。就会出现结果不一致的事情,为什么呢?因为显示的顺序跟数节点的两个顺序不一样,比如说,在xml里面有一组关键字一样的节点,在xml里面的顺序是abcd,但由于在for-each sorting的时候,因为加多了一个关键字,这组节点显示出来的顺序可能是cbda。ok,那会怎样呢,假设我们是看preceding来显示分组的标题吧。那么根据for-each sorting出来的结果,我们计出来的的preceding节点数是,c前面有2个,b前面有1个,d前面有3个,a前面为0个。而不是我们想要的0123,为什么?因为preceding看的还是用xml的node顺序来看的,即虽然显示的是cbda,但precding看还是看abcd来确定前面的节点数。这就会造成错误的结果。

对于这个,解决方法现在有两个,一个就是现在java里面sorting好了再出来,保证xml的节点顺序跟xsl sorting之后一样,就不会有问题,但这样做就背离了我们mvc的原则,即后台的数据处理不应该影响界面,而且这样修改起来很不好,因为实现同一功能的代码不应该写在两个地方。

再有一个就是不用count这个函数来计算当前或之后的节点数,直接把之前或之后的节点集传给for-each,即把preceding跟following当成一个distinct的功能,再一个个的显示出来,这样就避免了顺序不同的问题。可这种写法有点太过繁琐,特别是对于需要多次分组的情况,代码的写法要更复杂。

另外,对于preceding跟fowllowing的调用是非常消耗cpu资源的,特别是ms代码解析得也不是很好,对于四千多个节点,在for-each判断一次,将比一次I/O的时间还常,将近需要1分钟的时间,即是说,每次调用preceding跟fowllowing,系统都要整棵树走一下。确实不是很好的办法。

现在对于这种分组还没有更好的办法,瓶颈在于根本无法在xsl里面对变量重新赋值,如果可以就简单许多,现在尝试能否用template的方法来实现。

今天把template修改了一下,利用position来判断节点位置,然后判断是否应该进行分组,效率提高了很多,只用了原来三分之一的时间就可以完成。不过这种方法的缺点还是需要先在java里面sorting完再出来。

<xsl:template match="w:tc"> <fo:table-cell border="1pt solid black" padding="2pt" display-align="center"> <!-- 改进的列合并计算 --> <xsl:variable name="currentGridSpan" select="w:tcPr/w:gridSpan/@w:val"/> <xsl:variable name="adjustedGridSpan"> <xsl:choose> <xsl:when test="$currentGridSpan"> <xsl:value-of select="$currentGridSpan"/> </xsl:when> <xsl:otherwise>1</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 动态计算前置列跨度 --> <xsl:variable name="precedingSpanSum"> <xsl:choose> <xsl:when test="preceding-sibling::w:tc"> <xsl:value-of select="sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) - count(preceding-sibling::w:tc) + count(preceding-sibling::w:tc[not(w:tcPr/w:gridSpan)])"/> </xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 生成列合并属性 --> <xsl:if test="$currentGridSpan"> <xsl:attribute name="number-columns-spanned"> <xsl:value-of select="$currentGridSpan"/> </xsl:attribute> </xsl:if> <!-- 改进的行合并计算 --> <xsl:if test="w:tcPr/w:vMerge[@w:val='restart']"> <xsl:variable name="currentRow" select="ancestor::w:tr"/> <!-- 列位置计算考虑自身跨度 --> <xsl:variable name="currentPos" select="count(preceding-sibling::w:tc) + sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) - count(preceding-sibling::w:tc) + 1"/> <xsl:variable name="rowSpan"> <xsl:call-template name="calculateRowSpan"> <xsl:with-param name="remainingRows" select="$currentRow/following-sibling::w:tr"/> <xsl:with-param name="targetColumn" select="$currentPos"/> <xsl:with-param name="accumulator" select="1"/> </xsl:call-template> </xsl:variable> <xsl:attribute name="number-rows-spanned"> <xsl:value-of select="$rowSpan"/> </xsl:attribute> </xsl:if> <fo:block linefeed-treatment="ignore" white-space-collapse="true"> <xsl:apply-templates select=".//w:p"/> </fo:block> </fo:table-cell> </xsl:template> <!-- 新增行跨度计算模板 --> <xsl:template name="calculateRowSpan"> <xsl:param name="remainingRows"/> <xsl:param name="targetColumn"/> <xsl:param name="accumulator"/> <xsl:choose> <xsl:when test="$remainingRows[1]/w:tc[$targetColumn]/w:tcPr/w:vMerge[@w:val='continue']"> <xsl:call-template name="calculateRowSpan"> <xsl:with-param name="remainingRows" select="$remainingRows[position() > 1]"/> <xsl:with-param name="targetColumn" select="$targetColumn"/> <xsl:with-param name="accumulator" select="$accumulator + 1"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$accumulator"/> </xsl:otherwise> </xsl:choose> </xsl:template> 这个报错 The column-number or number of cells in the row overflows the number of fo:table-columns specified for the table. (See position 1:10790) 注意这是word的xml转xls-fo的xlst样式表,且版本均为1.0,请直接给出修改结果
03-08
<xsl:template match=“w:tc”><fo:table-cell border=“1pt solid black"padding=“2pt"display-align=“center”><xsl:variable name=“totalCols"select=“count(ancestor::w:tbl/w:tblGrid/w:gridCol)”/><xsl:variable name=“precedingSpanSum”>xsl:choose<xsl:when test=“preceding-sibling::w:tc”><xsl:value-of select=“sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) + count(preceding-sibling::w:tc[not(w:tcPr/w:gridSpan)])”/></xsl:when>xsl:otherwise0</xsl:otherwise></xsl:choose></xsl:variable><xsl:if test=“w:tcPr/w:gridSpan”><xsl:variable name=“declaredSpan"select=“w:tcPr/w:gridSpan/@w:val”/><xsl:variable name=“remainingCols"select=”$totalCols - $precedingSpanSum”/><xsl:attribute name=“number-columns-spanned”>xsl:choose<xsl:when test=”$declaredSpan > $remainingCols”><xsl:value-of select=”$remainingCols"/></xsl:when>xsl:otherwise<xsl:value-of select=“$declaredSpan”/></xsl:otherwise></xsl:choose></xsl:attribute></xsl:if><!–修正的行合并逻辑–><xsl:if test=“w:tcPr/w:vMerge[@w:val=‘restart’]”><xsl:variable name=“currentPos"select=“count(preceding-sibling::w:tc) + 1”/><xsl:variable name=“rowSpan”><xsl:call-template name=“calculateRowSpan”><!–修正点1:直接取后续所有行–><xsl:with-param name=“remainingRows"select=”…/following-sibling::w:tr”/><xsl:with-param name=“position"select=”$currentPos"/><xsl:with-param name=“count"select=“1”/></xsl:call-template></xsl:variable><xsl:attribute name=“number-rows-spanned”><xsl:value-of select=”$rowSpan"/></xsl:attribute></xsl:if><fo:block linefeed-treatment=“ignore"white-space-collapse=“true”><xsl:apply-templates select=”.//w:p"/></fo:block></fo:table-cell></xsl:template><!–新增递归模板–><xsl:template name=“calculateRowSpan”><xsl:param name=“remainingRows”/><xsl:param name=“position”/><xsl:param name=“count”/>xsl:choose<!–修正点2:增加空行判断–><xsl:when test=“count($remainingRows) = 0”><xsl:value-of select=“$count”/></xsl:when>xsl:otherwise<xsl:variable name=“currentRow"select=”$remainingRows[1]“/><xsl:variable name=“targetCell"select=”$currentRow/w:tc[$position]”/><!–修正点3:处理单元格位置偏移–><xsl:variable name=“precedingSpan"select=“sum($currentRow/w:tc[position() < $position]/w:tcPr/w:gridSpan/@w:val)”/>xsl:choose<!–修正点4:精准定位合并目标单元格–><xsl:when test=”$targetCell/w:tcPr/w:vMerge[@w:val=‘continue’] and ($position - $precedingSpan) <= count($currentRow/w:tc)“><xsl:call-template name=“calculateRowSpan”><xsl:with-param name=“remainingRows"select=”$remainingRows[position() > 1]”/><xsl:with-param name=“position"select=”$position"/><xsl:with-param name=“count"select=”$count + 1"/></xsl:call-template></xsl:when>xsl:otherwise<xsl:value-of select=“$count”/></xsl:otherwise></xsl:choose></xsl:otherwise></xsl:choose></xsl:template>修改这段样式表,使表格的行合并单元格生效,一定要注意这是word的xml转xls-fo的xlst样式表,且xslxml版本均为1.0,请直接给出修改结果
03-12
<xsl:template match=“w:tc”> <fo:table-cell border=“1pt solid black” padding=“2pt” display-align=“center”> <xsl:variable name=“totalCols” select=“count(ancestor::w:tbl/w:tblGrid/w:gridCol)”/> <xsl:variable name=“precedingSpanSum”> xsl:choose <xsl:when test=“preceding-sibling::w:tc”> <xsl:value-of select=“sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) + count(preceding-sibling::w:tc[not(w:tcPr/w:gridSpan)])”/> </xsl:when> xsl:otherwise0</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:if test=“w:tcPr/w:gridSpan”> <xsl:variable name=“declaredSpan” select=“w:tcPr/w:gridSpan/@w:val”/> <xsl:variable name=“remainingCols” select=“$totalCols - $precedingSpanSum”/> <xsl:attribute name=“number-columns-spanned”> xsl:choose <xsl:when test=“$declaredSpan > $remainingCols”> <xsl:value-of select=“$remainingCols”/> </xsl:when> xsl:otherwise <xsl:value-of select=“$declaredSpan”/> </xsl:otherwise> </xsl:choose> </xsl:attribute> </xsl:if> <xsl:if test="w:tcPr/w:vMerge[@w:val='restart']"> <xsl:variable name="currentPos" select="count(preceding-sibling::w:tc) + 1"/> <xsl:variable name="rowSpan"> <xsl:call-template name="calculateRowSpan"> <xsl:with-param name="remainingRows" select="../following-sibling::w:tr"/> <xsl:with-param name="position" select="$currentPos"/> <xsl:with-param name="count" select="1"/> </xsl:call-template> </xsl:variable> <xsl:attribute name="number-rows-spanned"> <xsl:value-of select="$rowSpan"/> </xsl:attribute> </xsl:if> <fo:block linefeed-treatment="ignore" white-space-collapse="true"> <xsl:apply-templates select=".//w:p"/> </fo:block> </fo:table-cell> </xsl:template> <!-- 新增递归模板 --> <xsl:template name="calculateRowSpan"> <xsl:param name="remainingRows"/> <xsl:param name="position"/> <xsl:param name="count"/> <xsl:choose> <xsl:when test="count($remainingRows) = 0"> <xsl:value-of select="$count"/> </xsl:when> <xsl:otherwise> <xsl:variable name="currentRow" select="$remainingRows[1]"/> <xsl:variable name="targetCell" select="$currentRow/w:tc[$position]"/> <xsl:variable name="precedingSpan" select="sum($currentRow/w:tc[position() < $position]/w:tcPr/w:gridSpan/@w:val)"/> <xsl:choose> <xsl:when test="$targetCell/w:tcPr/w:vMerge[@w:val='continue'] and ($position - $precedingSpan) <= count($currentRow/w:tc)"> <xsl:call-template name="calculateRowSpan"> <xsl:with-param name="remainingRows" select="$remainingRows[position() > 1]"/> <xsl:with-param name="position" select="$position"/> <xsl:with-param name="count" select="$count + 1"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$count"/> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:template>上述样式表中合并单元格有问题,你检查一下给出修改好的结果,一定要注意这是word的xml转xls-fo的xlst样式表,且xslxml版本均为1.0,请直接给出修改结果
03-11
<xsl:template match="w:tc"><fo:table-cell border="1pt solid black"padding="2pt"display-align="center"><!--列合并计算--><xsl:variable name="totalCols"select="count(ancestor::w:tbl/w:tblGrid/w:gridCol)"/><xsl:variable name="precedingSpanSum"><xsl:choose><xsl:when test="preceding-sibling::w:tc"><xsl:value-of select="sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) + count(preceding-sibling::w:tc[not(w:tcPr/w:gridSpan)])"/></xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable><!--处理列跨距--><xsl:if test="w:tcPr/w:gridSpan"><xsl:variable name="declaredSpan"select="w:tcPr/w:gridSpan/@w:val"/><xsl:variable name="remainingCols"select="$totalCols - $precedingSpanSum"/><xsl:attribute name="number-columns-spanned"><xsl:choose><xsl:when test="$declaredSpan > $remainingCols"><xsl:value-of select="$remainingCols"/></xsl:when><xsl:otherwise><xsl:value-of select="$declaredSpan"/></xsl:otherwise></xsl:choose></xsl:attribute></xsl:if><!--行合并修正--><xsl:if test="w:tcPr/w:vMerge[@w:val='restart']"><xsl:variable name="currentPos"select="count(preceding-sibling::w:tc) + 1"/><xsl:variable name="rowSpan"><xsl:call-template name="calculateRowSpan"><xsl:with-param name="remainingRows"select="../../following-sibling::w:tr"/><xsl:with-param name="position"select="$currentPos"/><xsl:with-param name="count"select="1"/></xsl:call-template></xsl:variable><xsl:attribute name="number-rows-spanned"><xsl:value-of select="$rowSpan"/></xsl:attribute></xsl:if><fo:block linefeed-treatment="ignore"white-space-collapse="true"><xsl:apply-templates select=".//w:p"/></fo:block></fo:table-cell></xsl:template><!--行跨距计算模板--><xsl:template name="calculateRowSpan"><xsl:param name="remainingRows"/><xsl:param name="position"/><xsl:param name="count"/><xsl:choose><xsl:when test="count($remainingRows) = 0"><xsl:value-of select="$count"/></xsl:when><xsl:otherwise><xsl:variable name="currentRow"select="$remainingRows[1]"/><!--计算前导列跨距--><xsl:variable name="precedingSpan"select="sum($currentRow/w:tc[position() < $position]/w:tcPr/w:gridSpan/@w:val)"/><!--计算实际目标位置--><xsl:variable name="adjustedPos"select="$position - $precedingSpan"/><xsl:choose><xsl:when test="$currentRow/w:tc[$adjustedPos]/w:tcPr/w:vMerge[@w:val='continue']"><xsl:call-template name="calculateRowSpan"><xsl:with-param name="remainingRows"select="$remainingRows[position() > 1]"/><xsl:with-param name="position"select="$position"/><xsl:with-param name="count"select="$count + 1"/></xsl:call-template></xsl:when><xsl:otherwise><xsl:value-of select="$count"/></xsl:otherwise></xsl:choose></xsl:otherwise></xsl:choose></xsl:template>修改这段样式表,使表格的行合并单元格生效,一定要注意这是word的xml转xls-fo的xlst样式表,且xlst版本为1.0,请直接给出修改结果
最新发布
03-12
<xsl:template match="w:tc"><fo:table-cell border="1pt solid black"padding="2pt"display-align="center"><xsl:variable name="totalCols"select="count(ancestor::w:tbl/w:tblGrid/w:gridCol)"/><xsl:variable name="precedingSpanSum"><xsl:choose><xsl:when test="preceding-sibling::w:tc"><xsl:value-of select="sum(preceding-sibling::w:tc/w:tcPr/w:gridSpan/@w:val) + count(preceding-sibling::w:tc[not(w:tcPr/w:gridSpan)])"/></xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable><xsl:if test="w:tcPr/w:gridSpan"><xsl:variable name="declaredSpan"select="w:tcPr/w:gridSpan/@w:val"/><xsl:variable name="remainingCols"select="$totalCols - $precedingSpanSum"/><xsl:attribute name="number-columns-spanned"><xsl:choose><xsl:when test="$declaredSpan > $remainingCols"><xsl:value-of select="$remainingCols"/></xsl:when><xsl:otherwise><xsl:value-of select="$declaredSpan"/></xsl:otherwise></xsl:choose></xsl:attribute></xsl:if><!--修正的行合并逻辑--><xsl:if test="w:tcPr/w:vMerge[@w:val='restart']"><xsl:variable name="currentPos"select="count(preceding-sibling::w:tc) + 1"/><xsl:variable name="rowSpan"><xsl:call-template name="calculateRowSpan"><!--修正点1:直接取后续所有行--><xsl:with-param name="remainingRows"select="../following-sibling::w:tr"/><xsl:with-param name="position"select="$currentPos"/><xsl:with-param name="count"select="1"/></xsl:call-template></xsl:variable><xsl:attribute name="number-rows-spanned"><xsl:value-of select="$rowSpan"/></xsl:attribute></xsl:if><fo:block linefeed-treatment="ignore"white-space-collapse="true"><xsl:apply-templates select=".//w:p"/></fo:block></fo:table-cell></xsl:template><!--新增递归模板--><xsl:template name="calculateRowSpan"><xsl:param name="remainingRows"/><xsl:param name="position"/><xsl:param name="count"/><xsl:choose><!--修正点2:增加空行判断--><xsl:when test="count($remainingRows) = 0"><xsl:value-of select="$count"/></xsl:when><xsl:otherwise><xsl:variable name="currentRow"select="$remainingRows[1]"/><xsl:variable name="targetCell"select="$currentRow/w:tc[$position]"/><!--修正点3:处理单元格位置偏移--><xsl:variable name="precedingSpan"select="sum($currentRow/w:tc[position() < $position]/w:tcPr/w:gridSpan/@w:val)"/><xsl:choose><!--修正点4:精准定位合并目标单元格--><xsl:when test="$targetCell/w:tcPr/w:vMerge[@w:val='continue'] and ($position - $precedingSpan) <= count($currentRow/w:tc)"><xsl:call-template name="calculateRowSpan"><xsl:with-param name="remainingRows"select="$remainingRows[position() > 1]"/><xsl:with-param name="position"select="$position"/><xsl:with-param name="count"select="$count + 1"/></xsl:call-template></xsl:when><xsl:otherwise><xsl:value-of select="$count"/></xsl:otherwise></xsl:choose></xsl:otherwise></xsl:choose></xsl:template>这个行合并失效,一定要注意这是word的xml转xls-fo的xlst样式表,且xslxml版本均为1.0,请直接给出修改结果
03-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值