我在XML中有一个很长的列表,我必须使用派生属性对其进行排序,该属性的计算选择存储在同一XML文件中的其他元素的值。可以这样做(最好使用XSLT 1.0)?
我发布了一个示例代码。
这是我的XML:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="example.xslt"?>
<example>
<references>
<value x="0" y="0" res="0" />
<value x="1" y="1" res="5" />
<value x="1" y="2" res="3" />
<value x="2" y="1" res="1" />
</references>
<list>
<item x="0" y="0">...</item>
<item x="1" y="2">...</item>
<item x="2" y="1">...</item>
</list>
</example>
这是XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<table>
<xsl:for-each select="//list/item">
<xsl:variable name="x">
<xsl:value-of select="@x" />
</xsl:variable>
<xsl:variable name="y">
<xsl:value-of select="@y" />
</xsl:variable>
<xsl:variable name="order">
<xsl:for-each select="//references/value">
<xsl:if test="@x=$x and @y=$y">
<xsl:value-of select="@res" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- I want to do something like this: -->
<!--<xsl:sort select="$order" />-->
<tr>
<td><xsl:value-of select="text()" /></td>
<td><xsl:value-of select="$order" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
感谢您的帮助!
答案 0 :(得分:3)
在XSLT 1.0中,排序键必须是单个XPath表达式,但正如其他人已经表明的那样,您可以在单个XPath表达式中做很多事情。在XSLT 2.0中,事情变得更容易,因为你的XPath表达式可以调用样式表函数来进行计算,你也可以使用xsl:sort体内的指令。你仍然无法在xsl:sort之前构造变量,但是 - 我们看了它并且它变得非常复杂。
关于代码的几点意见。
你不需要写这个:
<xsl:variable name="x">
<xsl:value-of select="@x" />
</xsl:variable>
当你可以写这个(这也可能更有效率):
<xsl:variable name="x" select="@x"/>
你不需要写这个:
<xsl:for-each select="//references/value">
<xsl:if test="@x=$x and @y=$y">
<xsl:value-of select="@res" />
</xsl:if>
</xsl:for-each>
什么时候可以这样写:
<xsl:value-of select="//references/value[@x=$x and @y=$y]/@res"/>
一般来说,你并没有充分利用该语言的全部力量。
答案 1 :(得分:1)
它并不像你想象的那么复杂。你需要的是current()
。
通过将“remote”节点与当前节点相匹配,您可以在单个XPath表达式中找到相关的value
,而不需要变量来捕获它们。因此,您可以在XSLT 1.0中直接进行排序:
<xsl:sort select="//references/value
[@x = current()/@x]
[@y = current()/@y]
/@res"
data-type="number" />
以下转换适用于您的输入
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<table>
<xsl:for-each select="//list/item">
<xsl:sort select="//references/value
[@x = current()/@x]
[@y = current()/@y]
/@res"
data-type="number" />
<xsl:variable name="order"
select="//references/value
[@x = current()/@x]
[@y = current()/@y]
/@res"/>
<tr>
<td><xsl:value-of select="text()" /></td>
<td><xsl:value-of select="$order" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
提供以下输出:
<html xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<table>
<tr>
<td>...</td>
<td>0</td>
</tr>
<tr>
<td>...</td>
<td>1</td>
</tr>
<tr>
<td>...</td>
<td>3</td>
</tr>
</table>
</body>
</html>
但是,如果要在模板中获取@res
的值,请注意您必须重复表达式。或者,您可以使用key
:
<xsl:stylesheet>
...
<xsl:key name="xy" match="value" use="concat(@x, ':', @y)" />
...
<xsl:for-each select="//list/item">
<xsl:sort select="key('xy', concat(@x, ':', @y))/@res" data-type="number" />
<xsl:variable name="order"
select="key('xy', concat(@x, ':', @y))/@res"/>
...
以相同的重复次数提供相同的输出。
答案 2 :(得分:1)
定义一个键<xsl:key name="k1" match="references/value" use="concat(@x, '|', @y)"/>
,然后简单地将<xsl:sort select="key('k1', concat(@x, '|', @y))/@res"/>
写为for-each的第一个子项。