开发者

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]) &gt; 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜