2

I am using XSL in Visual Studio 2010. I have the following *XSL*file, and I am attempting to use the tokenize() function to split a string:

<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' >
<xsl:output media-type="text/html; charset=UTF-8" encoding="UTF-8"/>

<xsl:template match='/'>
    <html>
        <head> </head>
        <body>
            <ul>
                <xsl:apply-templates select="response/result/doc"/>
            </ul>
        </body>
    </html>
</xsl:template>

<xsl:template match="doc">
    <xsl:variable name="title" select="str[@name='Title']"/>
    <xsl:variable name="features" select="tokenize(str[@name='Desc'],';')"/>
    <li>
        <xsl:value-of select="$title"/>
        <ul>
            <xsl:for-each select="$features">
                <li>
                    <xsl:value-of select="."/>
                </li>
            </xsl:for-each>

        </ul>
    </li>
</xsl:template>
</xsl:stylesheet>

Note: At this point I am unsure if I am actually using XSLT version 2.0. I think I do because I set it in the first line.

To the above XSL, I get the following error in Visual Studio 2010:

'tokenize()' is an unknown XSLT function.

I have the following input XML file:

<?xml version="1.0" encoding="UTF-8"?>
<response>
<result name="response" numFound="10000" start="0">
    <doc>
        <str name="Title">Title 1</str>
        <str name="Desc">Feature 1; Feature 2; Feature 3;</str>
    </doc>
    <doc>
        <str name="Title">Title 2</str>
        <str name="Desc">Feature 1; Feature 2; Feature 3;</str>
    </doc>
</result>
 </response>

Ideally, I would like to have an output like the HTML file below:

<html>
<head> </head>
<body>
    <ul>
        <li>Title 1
            <ul>
                <li>Feature 1</li>
                <li>Feature 2</li>
                <li>Feature 3</li>
            </ul>
        </li>
        <li>Title 2
            <ul>
                <li>Feature 1</li>
                <li>Feature 2</li>
                <li>Feature 3</li>
            </ul>
        </li>
    </ul>
</body>
 </html>

How do I tokenize() or split the string Desc in the XML file? Please ignore whitespace in this i.e. a little extra space before or after in the output file has no meaning since the output is HTML.

O.O.
  • 1,973
  • 6
  • 28
  • 40

2 Answers2

4

XSLT 2.0 is not natively supported in Visual Studio or .NET. When you try to execute an XSLT 2.0 stylesheet, you will get errors attempting to use 2.0 functions.

Option #1: The following XSLT 1.0 stylesheet uses a recursive template to achieve the same result without the tokenize() function:

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' >
    <xsl:output media-type="text/html; charset=UTF-8" encoding="UTF-8"/>

    <xsl:template match='/'>
        <html>
            <head> </head>
            <body>
                <ul>
                    <xsl:apply-templates select="response/result/doc"/>
                </ul>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="doc">
        <li>
            <xsl:value-of select="str[@name='Title']"/>
            <ul>
                <xsl:call-template name="listItem">
                    <xsl:with-param name="features" select="str[@name='Desc']"/>
                </xsl:call-template>
            </ul>
        </li>
    </xsl:template>

    <xsl:template name="listItem">
        <xsl:param name="features"/>
        <xsl:param name="delimiter" select="';'"/>
        <xsl:choose>
            <xsl:when test="contains($features, $delimiter)">
                <li>
                    <xsl:value-of select="normalize-space(
                                            substring-before($features, $delimiter))"/>
                </li>
                <xsl:variable name="nextValue" select="substring-after($features, 
                                                                       $delimiter)"/>
                <xsl:if test="normalize-space($nextValue)">
                    <xsl:call-template name="listItem">
                        <xsl:with-param name="features" select="$nextValue"/>
                        <xsl:with-param name="delimiter" select="$delimiter"/>
                    </xsl:call-template>    
                </xsl:if>
            </xsl:when>
            <xsl:otherwise>
                <li>
                    <xsl:value-of select="$features"/>
                </li>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Option #2: You should also be able to add the EXSLT.NET reference in Visual Studio and then would be able to use EXSLT str:tokenize() function:

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' 
    xmlns:str="http://exslt.org/strings" >
    <xsl:output media-type="text/html; charset=UTF-8" encoding="UTF-8"/>

    <xsl:template match='/'>
        <html>
            <head> </head>
            <body>
                <ul>
                    <xsl:apply-templates select="response/result/doc"/>
                </ul>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="doc">
        <li>
            <xsl:value-of select="str[@name='Title']"/>
            <ul>
                <xsl:for-each select="str:tokenize(str[@name='Desc'], ';')">
                    <li>
                        <xsl:value-of select="normalize-space(.)"/>
                    </li>
                </xsl:for-each>
            </ul>
        </li>
    </xsl:template>
</xsl:stylesheet>
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • 1
    Thank you very much **Mads**. Your first solution worked perfectly. The second solution gave me the error: *XslTransformException Cannot find a script or an extension object associated with namespace 'http://exslt.org/strings'*. I went to http://mvpxml.codeplex.com/ and put the DLL into my GAC. I’m unsure how to install EXSLT.NET into Visual Studio. The links on the page you gave me do not work. – O.O. Jun 28 '13 at 19:29
1

You are indicating in the first line that your stylesheet is expecting an XSLT 2.0 environment, but the error message indicates that you are using an XSLT 1.0 environment.

G. Ken Holman
  • 4,333
  • 16
  • 14
  • I put a **Note** in my original post that I am unsure if **Visual Studio** is using XSLT 1.0 or 2.0. How do I ensure **Visual Studio** is using 2.0, or can it? – O.O. Jun 27 '13 at 21:00
  • The XSLT function system-property('xsl:version') returns the implementation level of XSLT. I do not know the product. – G. Ken Holman Jun 28 '13 at 01:31
  • Visual Studio (and .NET) do not have native support for XSLT 2.0. If you want to use XSLT 2.0 you will need to use an XSLT 2.0 processor, such as Saxon, XQSharp, XmlPrime, etc. http://stackoverflow.com/questions/15783808/visual-studio-not-supporting-xslt-2-0 http://stackoverflow.com/questions/11205268/how-to-use-xslt-2-0-in-visual-studio-2010 – Mads Hansen Jun 28 '13 at 01:54
  • 1
    If you want to use XSLT 2.0 (and you do, it gives vast productivity benefits), then try oXygen or Stylus Studio for your development. Microsoft's XML tools are very out of date, and aren't being developed any further. – Michael Kay Jun 28 '13 at 08:06
  • Thank you Ken, Micheal and Mads. Unfortunately, I did not know that XSLT 1.0 is closely integrated with VS 2010. oXygen and Stylus Studio are a bit too expensive for me. If I do not have much luck with VS, I might look at Eclipse. – O.O. Jun 28 '13 at 19:33