I first had to learn the hard lesson of Result Tree Fragments and
dynamic XPath evaluation with XSLT 1.0, and then to infer the expected
behaviour of the msxsl:node-set() extension function. However, for
whatever reason when I use a variable in my select statement it
produces no results. When I hard-code one variant of the XPath into
the same call, it gives me exactly the results I'm after.
[[1]] Here's the entire XSL including the hard-coded node-set() call
that works:
<?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">
<xsl:output method="html" version="4.0"/>
<xsl:template match="ThreatModel">
<html>
<head>
<LINK href="styles.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mainSection">Details: Enumeration of Threats</div>
<br/>
<xsl:call-template name="CreateThreatsTable">
<xsl:with-param name="RiskResponse">Reduce</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="CreateThreatsTable">
<xsl:with-param name="RiskResponse">Transfer</xsl:with-
param>
</xsl:call-template>
</body>
</html>
</xsl:template>
<xsl:template name="CreateThreatsTable" match="/ThreatModel/
Threats">
<xsl:param name="RiskResponse"/>
<TABLE cellPadding="0" cellSpacing="1">
<TR>
<TD class="itemHeader" colspan="4">
Threat Analysis: Threats we have decided to <xsl:value-of
select="$RiskResponse"/>
</TD>
</TR>
<TR>
<TD class="subHeader">Threat</TD>
<TD class="subHeader">Risk_Severity</TD>
<TD class="subHeader">Threat_Category</TD>
<TD class="subHeader">Description</TD>
</TR>
<!-- This template filters the displayed records to those that
match the selected Risk Response -->
<xsl:call-template name="AddRowsToTable">
<xsl:with-param name="ThreatCategory">ConfidentialityThreat</
xsl:with-param>
<xsl:with-param name="RiskResponse">Reduce</xsl:with-param>
</xsl:call-template>
</TABLE>
<br/>
</xsl:template>
<xsl:template name="AddRowsToTable" match="/ThreatModel/Threats">
<xsl:param name="ThreatCategory"/>
<xsl:param name="RiskResponse"/>
<xsl:variable name="ThreatXPath">
<xsl:value-of select="concat('/ThreatModel/Threats/',
$ThreatCategory, 's/Threats/', $ThreatCategory)"/>
</xsl:variable>
<xsl:for-each select="/ThreatModel/Threats/ConfidentialityThreats/
Threats/ConfidentialityThreat[Risk = $RiskResponse]">
<TR>
<TD valign="top">
<xsl:value-of select="Name"/>
</TD>
<TD valign="top">
<xsl:value-of select="RiskRating"/>
</TD>
<TD valign="top">
<xsl:value-of select="../../Name"/>
</TD>
<TD valign="top">
<xsl:value-of select="Description"/>
</TD>
</TR>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
[[2]] Here's the call I'd *LIKE* to make in that XSL:
<xsl:for-each select="msxsl:node-set($ThreatXPath)[Risk =
$RiskResponse]" >
(Though I'd even settle for just <xsl:for-each select="msxsl:node-
set($ThreatXPath)" > )
[[3]] Here's an example XML fragment (representing a single Threat):
<?xml version="1.0" encoding="utf-8"?>
<ThreatModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Threats>
<Name>Threats</Name>
<ConfidentialityThreats>
<Name>Confidentiality Threats</Name>
<Threats>
<ConfidentialityThreat>
<Name>Attacker reboots device from external media and reads
sensitive data</Name>
<Description>This describes it nicely.</Description>
<Risk>Reduce</Risk>
<RiskRating>9</RiskRating>
</ConfidentialityThreat>
[[4]] Here's the output that [[1]] produces from [[3]] (without the
table formatting):
Details: Enumeration of Threats
Threat Analysis: Threats we have decided to Reduce
Threat Risk_Severity Threat_Category Description
Attacker reboots device from external media and reads sensitive data 9
Confidentiality Threats This describes it nicely.
Threat Analysis: Threats we have decided to Transfer
Threat Risk Severity Threat Category Description
[[5]] And here's the output that [[2]] produces from [[3]]:
Details: Enumeration of Threats
Threat Analysis: Threats we have decided to Reduce
Threat Risk_Severity Threat_Category Description
Threat Analysis: Threats we have decided to Transfer
Threat Risk Severity Threat Category Description
=====
I could swear I've done everything the same way that dozens of
newsgroup postings have indicated over the years - but still I'm vexed
by the same problem as everyone else seems to have. I've debugged
each statement (using VStudio 2005 and Stylus Studio 2007 XML
Professional), and I've tried all kinds of combinations of tricks I've
found on various newsgroups & forums that don't seem to help fix this
issue.
The operating environment is Windows XPSP2 with an IE-based
application (so I'm assuming MSXML and like 6.0).
Can anyone help me figure out what I'm missing here? This is driving
me nuts! I suspect it's something obvious, but after weeks of staring
at this code I can't see it. [Hell, I can hardly remember what it was
I was trying to achieve with this...]
Thanks, Mike
> <xsl:variable name="ThreatXPath">
> <xsl:value-of select="concat('/ThreatModel/Threats/',
> $ThreatCategory, 's/Threats/', $ThreatCategory)"/>
> </xsl:variable>
> [[2]] Here's the call I'd *LIKE* to make in that XSL:
>
> <xsl:for-each select="msxsl:node-set($ThreatXPath)[Risk =
> $RiskResponse]" >
I think you misunderstand what the node-set function is good for. It
converts a result tree fragment to a node-set. But your variable
$ThreatXPath is not a result tree fragment, it is a string with an XPath
expression.
--
Martin Honnen --- MVP XML
http://JavaScript.FAQTs.com/
I'm sure I *do* misunderstand. Do you have any suggestions for how I
could achieve what I want within the existing environment?
Thanks for your assistance, Mike
> I'm sure I *do* misunderstand. Do you have any suggestions for how I
> could achieve what I want within the existing environment?
It might work to simply use XPath expression alike
/ThreatModel/Threats/*[local-name() = concat($ThreatCategory,
's')]/Threats/*[local-name() = $ThreatCategory]
that way you are not able to dynamically evaluate XPath expressions in
general but for a limited set the check *[local-name() = $someVariable]
might suffice.
Thank you. And in case there's still room for misunderstanding: if
"$ThreatXPath is not a result tree fragment, it is a string with an
XPath
expression", is there some way I can rewrite my code to turn
$ThreatXPath into an RTF, so the node-set() I thought I was able to
achieve would work?
Or is the use of the local-name() function the closest I'd ever get?
Thanks again!
Mike
> Thank you. And in case there's still room for misunderstanding: if
> "$ThreatXPath is not a result tree fragment, it is a string with an
> XPath
> expression", is there some way I can rewrite my code to turn
> $ThreatXPath into an RTF, so the node-set() I thought I was able to
> achieve would work?
Well a variable bound to a result tree fragment looks like this:
<xsl:variable name="example">
<foo>
<bar/>
</foo>
</xsl:variable>
As you can see the contents of the xsl:variable element is nodes that
form a result tree fragment. Now you could use
msxsl:node-set($example)
to convert that result tree fragment into a node-set so that you can
apply all XPath operations on that node-set.
You original attempt seemed to put an XPath expression into the variable
and hoped to dynamically evaluate the XPath expression. That is not what
msxsl:node-set is good for. And MSXML does not provide another function
for that.
--