我要感谢这个社区最近帮助了我这么多。你是我的救星:)
我正在尝试验证数字(x)何时与前一个数字不同 所以我的清单已经分类了,首先我有:
<xsl:apply-templates select="house">
<xsl:sort select="./point/x"/>
</xsl:apply-templates>
重要的部分是:
<xsl:template match="house">
<xsl:if test="./point/x != preceding-sibling::house[1]/point/x">
<br/>
<xsl:value-of select="preceding-sibling::house[1]/point/x"/>
value changed to <xsl:value-of select="./point/x"/>
</xsl:if>
<!-- Just printing -->
<br/>
<td><xsl:value-of select="./point/x"/></td>
<td><xsl:value-of select="./point/y"/></td>
</xsl:template>
注意我在print,x和y中有2个值,但我只使用x
我希望输出类似于:
XY
00
01
0值更改为1
10个
11
不知怎的,我的结果得到了错误的前一个数字
00
1值变为0
01
0值变为1
10个
0值变为1
11
进度: 我发现通过删除排序输出是正确的
00
0值变为1
10个
1值变为0
01
0值变为1
11
所以现在我的问题是......我该怎么做这个程序?
答案 0 :(得分:3)
不是很好,但如果你必须知道“按照某个顺序的x的前一个值”,那么你必须再次订购节点并获取正确的节点:
<xsl:template match="house">
<xsl:variable name="current-position" select="position()" />
<xsl:variable name="preceding-x-ordered">
<xsl:for-each select="../house">
<xsl:sort select="point/x" />
<xsl:if test="position() = $current-position - 1">
<xsl:value-of select="point/x" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="position() > 1 and point/x != $preceding-x-ordered">
<br/>
<xsl:value-of select="$preceding-x-ordered"/>
<xsl:text> value changed to </xsl:text>
<xsl:value-of select="point/x"/>
</xsl:if>
<!-- Just printing -->
<br/>
<td><xsl:value-of select="point/x"/></td>
<td><xsl:value-of select="point/y"/></td>
</xsl:template>
XSLT应该是无副作用的,这意味着处理器可以按照自己的意愿处理模板,而不会改变整体结果。不是“知道”它在循环的最后一次迭代中所做的是该要求的结果。因此,preceding-sibling
轴无法知道之前处理的节点。
答案 1 :(得分:3)
这是一个更有效的(线性vs O(N ^ 2 * log(N))XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kHouseById" match="house" use="generate-id()"/>
<xsl:variable name="vIdsOfSorted">
<xsl:for-each select="/*/house">
<xsl:sort select="point/x" data-type="number"/>
<xsl:copy-of select="concat(generate-id(), ' ')"/>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/*">
<xsl:call-template name="traverseIds">
<xsl:with-param name="pIds" select="concat($vIdsOfSorted, ' ')"/>
<xsl:with-param name="pLength" select="count(house)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="traverseIds">
<xsl:param name="pIds"/>
<xsl:param name="pLength"/>
<xsl:param name="pPos" select="1"/>
<xsl:param name="pPrevId" select="''"/>
<xsl:if test="not($pPos > $pLength)">
<xsl:variable name="vId" select="substring-before($pIds, ' ')"/>
<xsl:variable name="vHouse" select="key('kHouseById', $vId)"/>
<xsl:value-of select=
"concat('
(', $vHouse/point/x, ',', $vHouse/point/y, ')')"/>
<xsl:if test="not($pPos >= $pLength)">
<xsl:variable name="vNextId" select=
"substring-before(substring-after($pIds, ' '), ' ')"/>
<xsl:variable name="vNextHouse" select="key('kHouseById', $vNextId)"/>
<xsl:if test="not($vHouse/point/x = $vNextHouse/point/x)">
<xsl:value-of select=
"concat('
value changed to ', $vNextHouse/point/x)"/>
</xsl:if>
<xsl:call-template name="traverseIds">
<xsl:with-param name="pIds" select="substring-after($pIds, ' ')"/>
<xsl:with-param name="pLength" select="$pLength"/>
<xsl:with-param name="pPos" select="$pPos+1"/>
<xsl:with-param name="pPrevId" select="$vId"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
对以下XML文档应用此转换时:
<t>
<house>
<point>
<x>1</x><y>0</y>
</point>
</house>
<house>
<point>
<x>0</x><y>1</y>
</point>
</house>
<house>
<point>
<x>0</x><y>0</y>
</point>
</house>
<house>
<point>
<x>1</x><y>1</y>
</point>
</house>
</t>
产生了想要的正确结果:
(0,1)
(0,0)
value changed to 1
(1,0)
(1,1)
如果你不喜欢递归,这里是一个使用xxx:node-set()
扩展函数的非递归XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output method="text"/>
<xsl:variable name="vrtfSorted">
<xsl:for-each select="/*/house">
<xsl:sort select="point/x" data-type="number"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vSorted" select="ext:node-set($vrtfSorted)/*"/>
<xsl:template match="/*">
<xsl:for-each select="$vSorted">
<xsl:variable name="vPos" select="position()"/>
<xsl:if test=
"$vPos > 1
and
not(point/x = $vSorted[position()=$vPos -1]/point/x)">
<xsl:text>
</xsl:text>
<xsl:value-of select="concat('value changed to ', point/x)"/>
</xsl:if>
<xsl:text>
</xsl:text>
<xsl:value-of select="concat('(',point/x,',',point/y, ')')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
产生完全相同的正确结果。
<强> II。 XSLT 2.0解决方案
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:variable name="vSorted">
<xsl:perform-sort select="house">
<xsl:sort select="xs:integer(point/x)"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select=
"(for $n in 2 to count($vSorted/*),
$n-1 in $n -1,
$vNewX in $vSorted/*[$n]/point/x,
$vOldX in $vSorted/*[$n-1]/point/x,
$vNewY in $vSorted/*[$n]/point/y,
$vOldY in $vSorted/*[$n-1]/point/y,
$remark in
if($vNewX ne $vOldX)
then concat('
value changed to ', $vNewX, '
')
else '
'
return
concat('(',$vOldX,',',$vOldY, ')', $remark)
),
concat('(',$vSorted/*[last()]/point/x,',',
$vSorted/*[last()]/point/y, ')'
)
"/>
</xsl:template>
</xsl:stylesheet>
当应用于同一XML文档(上图)时,会产生相同的正确结果:
(0,1)
(0,0)
value changed to 1
(1,0)
(1,1)
答案 2 :(得分:2)
这是另一种方法。你可能想要从群体的角度思考。正如你已经标记了问题xslt 2.0,这确实带来了一些有用的分组函数(在xslt 1.0中你必须使用Meunchian分组技术)
有效地,您按照 point / x 值对房屋进行分组,因此您可以获得每个不同的组:
<xsl:for-each-group select="house" group-by="point/x">
<xsl:sort select="point/x"/>
然后你可以像这样迭代组内的房子:
<xsl:for-each select="current-group()">
<xsl:sort select="point/y"/>
这是完整的XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:template match="houses">
<xsl:text>xy </xsl:text>
<xsl:for-each-group select="house" group-by="point/x">
<xsl:sort select="point/x"/>
<xsl:if test="position() > 1">
<xsl:value-of select="concat(' value changed to ', point/x, ' ')" />
</xsl:if>
<xsl:for-each select="current-group()">
<xsl:sort select="point/y"/>
<xsl:value-of select="concat(point/x, point/y, ' ')" />
</xsl:for-each>
<xsl:if test="position() < last()">
<xsl:value-of select="point/x" />
</xsl:if>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
应用于以下XML ....
<houses>
<house><point><x>1</x><y>0</y></point></house>
<house><point><x>2</x><y>1</y></point></house>
<house><point><x>0</x><y>1</y></point></house>
<house><point><x>1</x><y>1</y></point></house>
<house><point><x>0</x><y>0</y></point></house>
<house><point><x>2</x><y>0</y></point></house>
</houses>
以下是输出:
xy
00
01
0 value changed to 1
10
11
1 value changed to 2
20
21