Recursive XSL Templates in DVWPs

I’ve written quite a few recursive XSL templates recently, so I thought I’d do a post explaining what they are and how useful they can be.  On my current project, we’re using many Lookup Columns in Content Types which are based on items in Custom Lists.  These Lookup Columns also are almost all multi-select, meaning that the user can select zero to n of the values to categorize the item they are working with.

One of SharePoint’s quirks is how it then stores the values that the user has selected.  Generally, it looks like this: value1;value2;value3.  No problem, really, as long as you are OK with seeing the semicolon separated values in your displays.  However, if any of the values contains an apostrophe character (‘ or '), it will be stored in the column as ', like so: value1';value2;value3   (It’s actually that string without the “amp;” part, but I can’t get WordPress to let me show it without it converting to the ‘ character.)  SharePoint natively knows how to handle this, but if you are displaying the column in a DVWP, you may want to do a little work yourself to clean things up, and this is where a recursive XSL template comes in.  (Simply adding the disable-output-escaping=”yes” parameter will make the display look right, but here I want to use the values in the column for some filtering, so the ' is a problem.)

A recursive XSL template isn’t really all that different than a recursive function in any other language.  The template can call itself based on some condition multiple times as long as it is capable of “unwinding” itself at the end of the process.  In this example, we want to remove all of the ' strings and replace them with the ‘ character.  We don’t know how many of these strings there might be, nor do we want to care about it; we want the template to take care of it for us.

The template below works like this.  We call it, passing in the MultiSelectValue string that may or may not have the ' string in it.  The xsl:choose has an xsl:when condition that is true if the ' string is present.  If it is, we call the template again with the ' string snipped out (substring-before the string plus the apostrophe plus substring-after the string), and so on.  Once there are no more ' strings, the template will “unwind”, passing out the final string we want each time.

<xsl:template name="FixEscapedChars">
  <xsl:param name="MultiSelectValue"/>
  <xsl:variable name="Apostrophe">'</xsl:variable>
  <xsl:choose>
    <xsl:when test="contains($MultiSelectValue, '&amp;#39;')">
      <xsl:call-template name="FixEscapedChars">
        <xsl:with-param name="MultiSelectValue" select="concat(substring-before($MultiSelectValue, '&amp;#39;'), $Apostrophe, substring-after($MultiSelectValue, '&amp;#39;')) "/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$MultiSelectValue"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Note that you can handle other encoded characters by simply adding a new xsl:when clause for each to this template.

Similar Posts

11 Comments

  1. A clarification for those trying to make use of the above function:

    If I’m not mistaken, lines 5 8 and 10 above are incorrect — or at least, their visual display is. Note that all 3 lines appear to contain three consecutive apostrophes:

    ”’

    If you copy/paste them into SharePoint Designer, you’ll get errors about an unclosed string.

    In truth, they should (in SharePoint Designer) appear as a pair of apostrophes bracketing the following sequence of characters:
    – & (a single ampersand)
    – a
    – m
    – p
    – ; (a single semicolon)
    – # (a single hash sign)
    – 3
    – 9
    – ; (a single semicolon)

    That sequence mirrors the actual output of the SharePoint-rendered code.

  2. Hi Marc,

    Thank you so much for posting this. However due to my lack of knowledge in xsl, I wasnt able to implement this. Please could u tell me where I should paste this code?

    I have

    <a href="{@FileRef}" rel="nofollow"></a>

    every title works except the one that contains apostrophe.

    Example that works:
    title: HP Servers – New Z:\ Drive
    href: http://hav-fs64/ops/Lists/Case/HP%20Servers%20-%20New%20Z%20Drive

    Example that doesnt work:
    title: test’s
    href: http://hav-fs64/ops/Lists/Case/test's

    Thanks again.

    1. Syamsul:

      Your example *ought* to work, as the URL should be escaped by your browser, as you see in the first example you pasted. There, you can see that the spaces are replaced by %20 because 20 is the ASCII code for a space. In the case of the apostrophe, the encoding would be %39.

      The other thing that you should look at in this case is ddwrt:UrlEncode. Your line of code would change to:

      <a href="{ddwrt:UrlEncode(string(@FileRef))}" rel="nofollow"></a>

      In any case, I don’t think you need a recursive template here.

      M.

  3. I also had a problem fixing apostrophes in URLs. I fixed this using code from a response to one of Marc’s other forum answers (http://social.msdn.microsoft.com/Forums/en/sharepointcustomization/thread/684c9974-a5f4-42ec-9ab5-09c45bb3feeb), although it does sometimes give an error that “a string literal was not closed”:

    translate(@FileRef,''','%27')
    

    Despite having solved that particular problem, I’m very interested in how to implement these recursive XSL templates, but I just can’t understand how you do it. Are these equivalent to functions? If so, where do you call them and how do you do it?

    Thanks,

    Mark

    1. Mark:

      Recursive XSL templates are very similar to functions in other languages in that they call themselves until the task at hand is complete. There are some examples of recursive functions sprinkled throughout my blog, but there are also a bunch in my SPXSLT Codeplex Project as well.

      M.

      1. Thanks for the Codeplex link. I’ve downloaded them and look forward to using them!

        However, I still don’t understand where to put them and how to do the initial call. Is there a guide somewhere on how to utilize them?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.