Using XSLT to find nodes given a set of parameters
Sorry about the title — wasn’t sure how to word it.
Basically I have some XML like this:
<countries>
<country handle="bangladesh"/>
<country handle="india"/>
<country handle="pakistan"/>
</countries>
And some XSLT like this (which doesn’t work):
<xsl:template match="/countries">
<xsl:param name="popular"/>
<xsl:apply-templates select="country[count($popular/country[@handle = current()/@handle]) > 0]" />
</xsl:template>
<xsl:template match="/countries/country">
…
</xsl:template>
I want to pass in a list of popular destinations like this:
<popular>
<country handle="india"/>
<country handle="pakistan"/>
</popular>
…to the /countries template and have it only oper开发者_StackOverflow中文版ate on the ones in the $popular param. At the moment this simply does nothing. Changing the selector to country[true()] operates on them all, so at least I know the basic structure is right.
Any ideas? I think I may be getting confused by what is currently “current()”.
It's a lot simpler than you think:
<xsl:template match="/">
<popular>
<xsl:copy-of select="/countries/country[@handle=$popular/country/@handle]"/>
</popular>
</xsl:template>
Edit
The above was simply showing what was wrong with the OP's original XPath query. Here's a full working example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="popular"/>
<xsl:template match="/">
<xsl:apply-templates select="/countries">
<xsl:with-param name="popular" select="$popular"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/countries">
<xsl:param name="popular"/>
<countries>
<xsl:apply-templates select="country[@handle=$popular/country/@handle]"/>
</countries>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
...and a program to call it:
static void Main(string[] arguments)
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("xsltfile1.xslt");
XmlDocument d = new XmlDocument();
d.LoadXml(@"
<popular>
<country handle='india'/>
<country handle='xxx'/>
</popular>");
XsltArgumentList args = new XsltArgumentList();
args.AddParam("popular", "", d.DocumentElement);
xslt.Transform("xmlfile1.xml", args, Console.Out);
Console.ReadKey();
}
The solution to this problem is simple and straightforward (no need for string encoding or recursion).
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pPopular">
<country handle="india"/>
<country handle="pakistan"/>
</xsl:param>
<xsl:variable name="vPopular"
select="document('')/*/xsl:param[@name='pPopular']"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="country">
<xsl:if test="@handle = $vPopular/*/@handle">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<countries>
<country handle="bangladesh"/>
<country handle="india"/>
<country handle="pakistan"/>
</countries>
produces the wanted, correct result:
<countries>
<country handle="india"/>
<country handle="pakistan"/>
</countries>
Here's how you can do with a recursive template. You must pass the popular destinations as a comma separated list (like this: "'india,pakistan,'"
)
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/countries">
<xsl:param name="popular" select="'india,pakistan,'" />
<xsl:for-each select="country">
<xsl:call-template name="print-country-from-list">
<xsl:with-param name="pc" select="."/>
<xsl:with-param name="listc" select="$popular"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template match="/countries/country">
... <xsl:value-of select="@handle"/>
</xsl:template>
<xsl:template name="print-country-from-list">
<xsl:param name="pc"/>
<xsl:param name="listc" select="''"/>
<xsl:variable name="h" select="substring-before($listc, ',')"/>
<xsl:if test="$h">
<xsl:choose>
<xsl:when test="$pc/@handle=$h">
<xsl:apply-templates select="$pc" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="print-country-from-list">
<xsl:with-param name="pc" select="$pc"/>
<xsl:with-param name="listc" select="substring-after($listc, ',')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The output is the 2 countries.
精彩评论