Using xsl variable to capture output of call-templ

2020-07-07 01:15发布

问题:

I have seen lots of posts that do something like this and that makes me feel like this is possible and I am just doing something wrong. I have simplified it as much as possible to try and figure out why this is happening:

Heres my xml (nothing very exciting):

<?xml version="1.0" encoding="UTF-8"?>
<REPORT>

</REPORT>

Here is my xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:template match="REPORT">
      <xsl:variable name="tryThisTemplate">
        <xsl:call-template name="TRY_THIS"/>
      </xsl:variable>
      <TEST1>
        <xsl:call-template name="TRY_THIS"/>
      </TEST1>
      <TEST2>
        <xsl:value-of disable-output-escaping="yes" select="$tryThisTemplate" />
      </TEST2>
      <TEST3>
        <xsl:value-of select="$tryThisTemplate" />
      </TEST3>
    </xsl:template>

    <xsl:template name="TRY_THIS">
      <MY_NODE desc="my description" />
    </xsl:template>
</xsl:stylesheet>

Here is my result:

<?xml version="1.0" encoding="utf-8"?>  
<TEST1>
  <MY_NODE desc="my description"/>
</TEST1>
<TEST2></TEST2>
<TEST3></TEST3>

Here is my question: How come TEST2 and TEST3 don't work. The $tryThisTemplate variable appears to be blank. Am I misunderstanding something here. Should I be doing this in a different way?

回答1:

Here is the correct way to do this (note that DOE is not necessary and should be avoided):

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="REPORT">
    <xsl:variable name="tryThisTemplate">
        <xsl:call-template name="TRY_THIS"/>
    </xsl:variable>
    <TEST1>
        <xsl:call-template name="TRY_THIS"/>
    </TEST1>
    <TEST2>
        <xsl:copy-of select="$tryThisTemplate" />
    </TEST2>
    <TEST3>
        <xsl:copy-of select="$tryThisTemplate" />
    </TEST3>
 </xsl:template>

 <xsl:template name="TRY_THIS">
    <MY_NODE desc="my description" />
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<REPORT>

</REPORT>

the wanted result is produced:

<TEST1>
   <MY_NODE desc="my description"/>
</TEST1>
<TEST2>
   <MY_NODE desc="my description"/>
</TEST2>
<TEST3>
   <MY_NODE desc="my description"/>
</TEST3>

Explanation: <xsl:copy-of> copies (as its name says) nodes. <xsl:value-of> outputs the string value of whatever is in its select attribute. The string value of an element is the concatenation (in document order) of all of its text-node descendents. In your case the element has no text-node descendents and thus <xsl:value-of> outputs nothing.



回答2:

Yes there is a misunderstanding here. If you are trying to copy the structure of $tryThisTemplate to the output, you need to use <xsl:copy-of> instead of <xsl:value-of>. <xsl:value-of> outputs the string value of its select argument, that is, its text content, which in this case is an empty string.



回答3:

The $tryThisTemplate variable appears to be blank

The variable it's not blank, but with xsl:value-of you are asking for the text nodes inside that. That's "blank".

For instance, try with:

  <TEST3>
    <xsl:copy-of select="$tryThisTemplate" />
  </TEST3>

And you'll see magically appear MY_NODE between TEST3 :))



回答4:

check out my solution

here is my template (was veriable content)

<xsl:template name="author-name" match="string-name">
    <xsl:if test="fn[string-length(normalize-space()) > 0] or given-names[string-length(normalize-space()) > 0]">
        <xsl:apply-templates select="fn | given-names" mode="ascii"/>
    </xsl:if>
    <xsl:if test="sn[string-length(normalize-space()) > 0] or surname[string-length(normalize-space()) > 0]">
        <xsl:text> </xsl:text><xsl:apply-templates select="sn | surname" mode="ascii"/>
    </xsl:if>
    <xsl:if test="sn[string-length(normalize-space()) > 0] or suffix[string-length(normalize-space()) > 0]">
        <xsl:text> </xsl:text><xsl:apply-templates select="sn | suffix" mode="ascii"/>
    </xsl:if>
</xsl:template>

and when I use it in any other template simply I just call it like this

<xsl:template match="string-name">
    <xsl:variable name="author">
        <xsl:call-template name="author-name"/> <!--here is the tricky part-->
    </xsl:variable>
    <span class="NLM_{name()}">
        <xsl:copy-of select="@id" />
        <xsl:value-of select="normalize-space($author)" />
    </span>   
</xsl:template>

hope this helps you