从这个XML来源:
<?xml version="1.0" encoding="utf-8" ?>
<ROOT>
<STRUCT>
<COL order="1" nodeName="FOO/BAR" colName="Foo Bar" />
<COL order="2" nodeName="FIZZ" colName="Fizz" />
</STRUCT>
<DATASET>
<DATA>
<FIZZ>testFizz</FIZZ>
<FOO>
<BAR>testBar</BAR>
<LIB>testLib</LIB>
</FOO>
</DATA>
<DATA>
<FIZZ>testFizz2</FIZZ>
<FOO>
<BAR>testBar2</BAR>
<LIB>testLib2</LIB>
</FOO>
</DATA>
</DATASET>
</ROOT>
我想生成这个HTML:
<html>
<head>
<title>Test</title>
</head>
<body>
<table border="1">
<tr>
<td>Foo Bar</td>
<td>Fizz</td>
</tr>
<tr>
<td>testBar</td>
<td>testFizz</td>
</tr>
<tr>
<td>testBar2</td>
<td>testFizz2</td>
</tr>
</table>
</body>
</html>
这是我目前拥有的XSLT:
<?xml version="1.0" encoding="utf-8"?>
<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:output method="html" indent="yes"/>
<xsl:template match="/ROOT">
<html>
<head>
<title>Test</title>
</head>
<body>
<table border="1">
<tr>
<!--Generate the table header-->
<xsl:apply-templates select="STRUCT/COL">
<xsl:sort data-type="number" select="@order"/>
</xsl:apply-templates>
</tr>
<xsl:apply-templates select="DATASET/DATA" />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="COL">
<!--Template for generating the table header-->
<td>
<xsl:value-of select="@colName"/>
</td>
</xsl:template>
<xsl:template match="DATA">
<xsl:variable name="pos" select="position()" />
<tr>
<xsl:for-each select="/ROOT/STRUCT/COL">
<xsl:sort data-type="number" select="@order"/>
<xsl:variable name="elementName" select="@nodeName" />
<td>
<xsl:value-of select="/ROOT/DATASET/DATA[$pos]/*[name() = $elementName]" />
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
它几乎可以工作,我遇到的问题是从STRUCT块的“nodeName”属性值中指定的路径中检索正确的DATA节点。
答案 0 :(得分:7)
这是一个不使用任何扩展程序的纯XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
<table border="1">
<xsl:apply-templates select="*/STRUCT"/>
<xsl:apply-templates select="*/DATASET/DATA"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="STRUCT">
<tr>
<xsl:apply-templates select="COL"/>
</tr>
</xsl:template>
<xsl:template match="COL">
<td><xsl:value-of select="@colName"/></td>
</xsl:template>
<xsl:template match="DATA">
<tr>
<xsl:apply-templates select="/*/STRUCT/*/@nodeName">
<xsl:with-param name="pCurrentNode" select="."/>
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="@nodeName" name="getNodeValue">
<xsl:param name="pExpression" select="string(.)"/>
<xsl:param name="pCurrentNode"/>
<xsl:choose>
<xsl:when test="not(contains($pExpression, '/'))">
<td><xsl:value-of select="$pCurrentNode/*[name()=$pExpression]"/></td>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="getNodeValue">
<xsl:with-param name="pExpression"
select="substring-after($pExpression, '/')"/>
<xsl:with-param name="pCurrentNode" select=
"$pCurrentNode/*[name()=substring-before($pExpression, '/')]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
将此转换应用于提供的XML文档:
<ROOT>
<STRUCT>
<COL order="1" nodeName="FOO/BAR" colName="Foo Bar" />
<COL order="2" nodeName="FIZZ" colName="Fizz" />
</STRUCT>
<DATASET>
<DATA>
<FIZZ>testFizz</FIZZ>
<FOO>
<BAR>testBar</BAR>
<LIB>testLib</LIB>
</FOO>
</DATA>
<DATA>
<FIZZ>testFizz2</FIZZ>
<FOO>
<BAR>testBar2</BAR>
<LIB>testLib2</LIB>
</FOO>
</DATA>
</DATASET>
</ROOT>
产生了想要的正确结果:
<html>
<head>
<title>Test</title>
</head>
<body>
<table border="1">
<tr>
<td>Foo Bar</td>
<td>Fizz</td>
</tr>
<tr>
<td>testBar</td>
<td>testFizz</td>
</tr>
<tr>
<td>testBar2</td>
<td>testFizz2</td>
</tr>
</table>
</body>
</html>
答案 1 :(得分:2)
你得到的问题:
<xsl:variable name="elementName" select="@nodeName" />
...
<xsl:value-of select="/ROOT/DATASET/DATA[$pos]/*[name() = $elementName]" />
是假设elementName
只是单个元素的名称。如果它是一个任意的XPath表达式,测试将失败。
您将遇到(或可能已经遇到)的第二个问题是select子句中不允许使用属性值模板,因此您不能执行以下简单的操作:
<xsl:value-of select="/ROOT/DATASET/DATA[$pos]/{$elementName}" />
您需要的是能够为您正在寻找的元素动态创建XPath表达式,然后动态评估该表达式。
对于解决方案,我转向evaluate()
库中的EXSLT的dynamic
函数。我不得不使用它两次:一次构建表示查询的整个XPath表达式,一次用于评估该查询。这种方法的优点是可以访问evaluate
的完整XPath解析和执行功能。
<xsl:variable name="elementLocation" select="@nodeName" />
<xsl:variable name="query" select="concat('/ROOT/DATASET/DATA[$pos]/',
dyn:evaluate('$elementLocation'))"/>
...
<xsl:value-of select="dyn:evaluate($query)"/>
其中dyn
命名空间被声明为http://exslt.org/dynamic
。找出这里引用的地方很棘手,并且我试了好几次。
使用这些而不是你的elementName和value-of表达式,我得到:
<html xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:dyn="http://exslt.org/dynamic">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test</title>
</head>
<body><table border="1">
<tr>
<td>Foo Bar</td>
<td>Fizz</td>
</tr>
<tr>
<td>testBar</td>
<td>testFizz</td>
</tr>
<tr>
<td>testBar2</td>
<td>testFizz2</td>
</tr>
</table></body>
</html>
这是我认为你正在寻找的。 p>
不幸的是,我不熟悉MSXML,所以我不能告诉你特定的XSLT处理器是否支持这种扩展或类似的东西。
答案 2 :(得分:0)
欧文,
使用dyn:evaluate>()
的解决方案很好,但在浏览器中不起作用,请参见此处:
http://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/201008/msg00126.html
你得到的问题: ... 是假设elementName只是一个元素的名称。 如果它是一个任意的XPath表达式,测试将失败。
Dimitrie的解决方案不适用于一般的XPath解析和处理
通过添加可以简单地将多个节点添加到他的解决方案中
<xsl:for-each ...>
s,请参见下面的差异:
$ diff -u x.xsl y.xsl --- x.xsl 2010-08-13 14:53:42.000000000 +0200 +++ y.xsl 2010-08-14 11:59:42.000000000 +0200 @@ -40,15 +40,19 @@ <xsl:choose> <xsl:when test="not(contains($pExpression, '/'))"> - <td><xsl:value-of select="$pCurrentNode/*[name()=$pExpression]"/></td> + <xsl:for-each select="$pCurrentNode/*[name()=$pExpression]"> + <td><xsl:value-of select="."/></td> + </xsl:for-each> </xsl:when> <xsl:otherwise> + <xsl:for-each select="$pCurrentNode/*[name()=substring-before($pExpression, '/')]"> <xsl:call-template name="getNodeValue"> <xsl:with-param name="pExpression" select="substring-after($pExpression, '/')"/> <xsl:with-param name="pCurrentNode" select= - "$pCurrentNode/*[name()=substring-before($pExpression, '/')]"/> + "."/> </xsl:call-template> + </xsl:for-each> </xsl:otherwise> </xsl:choose> $