From this XML source :
<?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>
I want to generate this 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>
Here is the XSLT I currently have :
<?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>
It almost works, the problem I have is to retrieve the correct DATA node from the path specified in the "nodeName" attribute value of the STRUCT block.
Owen,
your solution making use of
dyn:evaluate>()
is fine, but does not work in browsers, see here:http://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/201008/msg00126.html
Dimitrie's solution was not for general XPath parsing, and the handling of more than one node can simply be added to his solution by adding
<xsl:for-each ...>
s, see the diff below:Here is a pure XSLT 1.0 solution that doesn't use any extensions:
when this transformation is applied on the provided XML document:
the wanted, correct result is produced:
The problem with what you've got:
is that it assumes
elementName
is only a single element's name. If it's an arbitrary XPath expression, the test will fail.The second problem you'll run into (or probably already have) is that attribute value templates are not allowed in select clauses, so you can't do something simple like this:
What you need is something that will dynamically create the XPath expression to the element you're looking for, and then dynamically evaluate that expression.
For a solution, I turned to EXSLT's
evaluate()
function, in thedynamic
library. I had to use it twice: once to build up the entire XPath expression representing the query, and once to evaluate that query. The advantage of this approach is that you get access toevaluate
's full XPath parsing and execution capabilities.where the
dyn
namespace is declared up top ashttp://exslt.org/dynamic
. Figuring out where to quote here is tricky and took me several tries to get right.Using these instead of your elementName and value-of expressions, I get:
which is what I think you're looking for.
Unfortunately, I'm not versed in MSXML, so I can't tell you whether your specific XSLT processor supports this extension or something similar.