我刚开始使用 XSLT 我想知道我是否可以这样做:
我们正在使用这个例子 http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog并稍微修改一下。
将以下内容粘贴到 XSLT 区域。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<xsl:choose>
<xsl:when test="country = 'USA'">
<tr>
<td>Title:</td> <td><xsl:value-of select="title"/></td>
<td>Artist</td> <td><xsl:value-of select="artist"/></td>
<td>Year:</td> <td><xsl:value-of select="year"/></td>
</tr>
</xsl:when>
<xsl:otherwise>
<tr>
<td>Price:</td> <td><xsl:value-of select="price"/></td>
<td>Company:</td> <td><xsl:value-of select="company"/></td>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
我们这里得到的是一个通用的 XML 源,但是根据 XML 元素的一个节点(在本例中为国家/地区),我们希望以不同方式显示数据。
到目前为止一切顺利。
现在我们要做的是使用 XML 结构来指定要根据国家/地区显示的每个标签到节点对。然后使用 for-each 循环遍历所有对并显示它们。
这样做的理由是,格式可能比简单的 LabelValue 复杂一点,我们以后不必手动更改所有这些。
这是我试过的。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="Details">
<Details>
<USA>
<Pair><Node>title</Node><Label>Title:</Label></Pair>
<Pair><Node>artist</Node><Label>Artist:</Label></Pair>
<Pair><Node>year</Node><Label>Year:</Label></Pair>
</USA>
<Others>
<Pair><Node>price</Node><Label>Price:</Label></Pair>
<Pair><Node>company</Node><Label>Company:</Label></Pair>
</Others>
</Details>
</xsl:variable>
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<xsl:choose>
<xsl:when test="country = 'USA'">
<xsl:for-each select = "$Details/Details/USA/Pair">
<xsl:variable name="Node">
<xsl:value-of select ="Node"/>
</xsl:variable>
<tr><td><xsl:value-of select = "Label"/></td><td> <xsl:value-of select ="Node"/></td></tr>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select = "$Details/Details/Others/Pair">
<xsl:variable name="Node">
<xsl:value-of select ="Node"/>
</xsl:variable>
<tr><td><xsl:value-of select = "Label"/></td><td> <xsl:value-of select ="Node"/></td></tr>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
但这行不通。
有人可以为我指明正确的方向吗?
最佳答案
问题出在 XSLT 1.0 中,$Details 变量包含一个“结果树片段”,而不是一个“节点集”,XSLT 需要它是一个节点集才能访问它按照你要求的方式。要在 XSLT 1.0 中解决这个问题,您需要使用扩展函数将结果树片段转换为节点集。您使用哪种扩展功能取决于您当前的平台。对于此示例,我使用的是 Micorsoft,但 Microsoft 之外最常见的软件是 EXSLT。 (参见 http://www.xml.com/pub/a/2003/07/16/nodeset.html)
通过定义一个扩展函数,你就可以做到这一点
<xsl:for-each select="msxsl:node-set($Details)/Details/USA/Pair">
不过,您仍然需要进行一些调整才能实现您的目标。在此循环中,您现在将位于 Pair 元素上,但您需要在外循环中引用回原始 cd。为此,您需要首先定义一个变量来保存对 cd 的引用(这需要在内循环之前进行)。
<xsl:variable name="cd" select="." />
然后,在内部循环中,您可以这样做以获取相关字段
<xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>
值得一提的是,使用您的新代码,您有机会减少代码重复。您可以放弃 xsl:choose,而只使用一个内部 xsl:for-each
<xsl:for-each
select="$Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">
这将选择一个匹配的国家(如果存在),否则将使用“其他”
这是完整的 XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<xsl:variable name="Details">
<Details>
<USA>
<Pair><Node>title</Node><Label>Title:</Label></Pair>
<Pair><Node>artist</Node><Label>Artist:</Label></Pair>
<Pair><Node>year</Node><Label>Year:</Label></Pair>
</USA>
<Others>
<Pair><Node>price</Node><Label>Price:</Label></Pair>
<Pair><Node>company</Node><Label>Company:</Label></Pair>
</Others>
</Details>
</xsl:variable>
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<xsl:variable name="cd" select="." />
<xsl:for-each select="msxsl:node-set($Details)/Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">
<tr>
<td>
<xsl:value-of select = "Label"/>
</td>
<td>
<xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
在 XSLT 2.0 中,情况有所不同,$Details 包含一个“序列”,您不需要使用扩展函数。
还有另一种方法可以实现这一点,无需扩展函数,即使用document() 函数让XSTL 引用自身。您不只是拥有一个变量,而是只包含一个 XML 片段
<my:Details>
<Details>
...
</Details>
</my:Details>
然后你可以像这样声明一个变量来引用它。因为您直接引用输入文档,所以这被视为节点集。
<xsl:variable name="Details" select="document('')/*/my:Details" />
也试试这个 XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" exclude-result-prefixes="my">
<my:Details>
<Details>
<USA>
<Pair>
<Node>title</Node>
<Label>Title:</Label>
</Pair>
<Pair>
<Node>artist</Node>
<Label>Artist:</Label>
</Pair>
<Pair>
<Node>year</Node>
<Label>Year:</Label>
</Pair>
</USA>
<Others>
<Pair>
<Node>price</Node>
<Label>Price:</Label>
</Pair>
<Pair>
<Node>company</Node>
<Label>Company:</Label>
</Pair>
</Others>
</Details>
</my:Details>
<xsl:template match="/">
<xsl:variable name="Details" select="document('')/*/my:Details" />
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<xsl:variable name="cd" select="." />
<xsl:for-each select = "$Details/Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">
<tr>
<td>
<xsl:value-of select = "Label"/>
</td>
<td>
<xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
关于xml - 使用 xsl :for-each over an xml to decide which label-value pairs are displayed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18973359/