XSLT - Best way to split and render comma separate

2020-02-01 08:58发布

问题:

I have some data in an XML element that looks like this:

<item value="category1,category2">Item Name</item>

The bit I'm interested in is the value attribute. I'm able to get the data contained in this attribute into a template which looks like this:

<xsl:template name="RenderValues">
    <xsl:param name="ValueList" />
    <xsl:value-of select="$ValueList" /> <!-- outputs category1,category2-->
</xsl:template>

What I want to do is to process the comma separated values in an efficient manner. What is the best way to render something like the following from inside the RenderValues template?

<a href="x.asp?item=category1">category1</a>
<a href="x.asp?item=category2">category2</a>

回答1:

In XSLT 2.0/XPath 2.0 use the standard XPath 2.0 function tokenize().

In XSLT 1.0 one needs either to write a recursively called template or, more conveniently, use the str-split-to-words function/template of the FXSL library.

Here is an example of the latter:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"

>
<!--                                                 -->
   <xsl:import href="strSplit-to-Words.xsl"/>
<!--                                                 -->
   <xsl:output indent="yes" omit-xml-declaration="yes"/>
<!--                                                 -->
    <xsl:template match="/*">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-to-words">
          <xsl:with-param name="pStr" select="string(@value)"/>
          <xsl:with-param name="pDelimiters" 
                          select="', &#10;'"/>
        </xsl:call-template>
      </xsl:variable>
<!--                                                 -->
      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>
<!--                                                 -->
    <xsl:template match="word" priority="10">
      <a href="x.asp?item={.}"><xsl:value-of select="."/></a>
    </xsl:template>
<!--                                                 -->
</xsl:stylesheet>

When the above transformation is applied on the provided XML document:

<item value="category1,category2">Item Name</item>

the wanted result is produced:

<a href="x.asp?item=category1" xmlns:ext="http://exslt.org/common">category1</a>
<a href="x.asp?item=category2" xmlns:ext="http://exslt.org/common">category2</a>

The pDelimiters parameter of this template allow multiple delimiting characters to be specified. In the above example, any separating character can be either a comma, a space or a new line character.



回答2:

You're looking for the tokenize function:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:str="http://exslt.org/common">

<xsl:template match="/">
<xsl:variable name="sampleString">XML,XSLT,XPath,SVG,XPointer</xsl:variable>
<xsl:for-each select="str:tokenize($sampleString,',')">
    <a>
        <xsl:attribute name="href">
            <xsl:value-of select="str:concat('x.asp?item=', .)" />
        </xsl:attribute>
    <xsl:value-of select="."/>
    </a>
</xsl:for-each>
</xsl:template>

</xsl:stylesheet>

http://www.xml.com/pub/a/2003/05/07/tr.html



回答3:

It's a legitimate question, and the answers provided are good ones. But if you're using XSLT 1.0, and you can't use extension functions, it's simply not possible. XSLT templates produce result tree fragments, and XSLT can only work on node sets. Tokenizing in XSLT requires recursion, which means calling a template, which means producing a data structure that you can't process.

If this limitation applies to you - and really, even if it doesn't - you should, if possible, solve this problem by finding the person who's generating XML whose content has to be parsed again after the DOM has already parsed it and make him or her stop. Putting multiple values in an attribute is just plain wrong.



标签: xml xslt