How can I apply an xslt template to a string?

2019-07-03 08:34发布

问题:

Given a template used for building some html around a value, I want to pass in a string, rather than a node set. As an example I want to concat some values and pass that to the template. How can I achieve that sort of thing?

<xsl:template match="text()" mode="kvp-print-single">
    <tr>
        <td colspan="3"><xsl:value-of select="."/></td>
    </tr>
</xsl:template>

...

<xsl:apply-templates select="concat=(haba/hiba:text(), ' - ', huba/baba:text())" mode="kvp-print-single"/>

ErrorMsg: xml or stylesheet file is invalid!

Exception: System.Xml.Xsl.XsltException: Expression must evaluate to a node-set.

回答1:

If the aim is code re-use, to use the template in multiple places, then what you could do is give your template a name (in addition to the template match), and give it a default parameter

<xsl:template match="text()" name="kvp-print-single" mode="kvp-print-single">
    <xsl:param name="text" select="." />
    <tr>
        <td colspan="3"><xsl:value-of select="$text"/></td>
    </tr>
</xsl:template>

Then just use xsl:call-template to call it with you concatenated string as a parameter

<xsl:call-template name="kvp-print-single">
   <xsl:with-param name="text" select="concat(haba/hiba:text(), ' - ', huba/baba:text())" />
</xsl:call-template>

Note, the template will still match "text()" nodes in the normal way when matched using xsl:apply-templates.



回答2:

You could use call-template and a named template, rather than apply-templates, thus:

<xsl:template name="kvp-print-single">
  <xsl:param name="theValue"/>
   <tr>
        <td colspan="3"><xsl:value-of select="$theValue"/></td>
    </tr>
</xsl:template>

<xsl:call-template name="kvp-print-single">
   <xsl:with-param name="theValue" select="concat(haba/hiba:text(), ' - ', huba/baba:text())"/>
</xsl:call-template>

The point of apply-templates is to take a nodeset, and apply the most appropriate template to each node in turn. call-template and named templates allows you to break up your XSLT into more manageable chunks, without changing the context.



回答3:

You can't "pass in a string, rather than a node set" because templates are not called like functions. With XSLT, not the code controls the execution order but the data does by matching templates. It is possible to use named templates that can be called instead matched, but to pass values, you can use parameters for each templates.

In your case, you don't even have to as you can adress the text parts (given haba/hiba is the address) like so:

<xsl:template match="some_element" mode="kvp-print-single">
  <tr>
    <td colspan="3">
      <xsl:value-of select="concat=(/root/haba/hiba/text(), ' - ', /root/huba/baba/text())"/>
    </td>
  </tr>
</xsl:template>  

The adress needs to be correct XPath, of course (absolute or even relative to matching element).

@Mithon: The other answers require parameters. Use as many modularized templates as you want, but why would you want to add parameters if you don't need them?