Question

I am using XSLT 2.0 to transform XML to HTML. I am trying to split the attribute value at every 6th character.

My source XML looks like this:

<item effrg="521529577580620621623624628628631631642645" />

My current (failed XSLT) looks like this:

<xsl:analyze-string regex=".{{6}}" select="item/@effrg">
     <xsl:matching-substring><xsl:value-of select="."/></xsl:matching-substring>
     <xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
</xsl:analyze-string>

My desired output should be:

521529 577580 620621 623624 628628 631631 642645

Am I on the right track? Can anyone help?

Was it helpful?

Solution 4

To split the source string you provided you can use a simpler expression:

<xsl:value-of select="replace(item/@effrg, '(.{6}){7}', '$1 $2 $3 $4 $5 $6 $7')" />

in the context where you are running <xsl:analyze-string>.

With <xsl:analyze-string> you can get each $n group with regex-group(n). You could then loop in XSLT concatenating the spaces in recursive call-templates.

But you can also loop more concisely in XPath 2.0 using for and string-join to obtain the desired result:

<xsl:template match="item">
    <xsl:variable name="size" select="floor(string-length(@effrg) div 6)"></xsl:variable>
    <xsl:analyze-string regex="(.{{6}}){{{$size}}}" select="@effrg">
        <xsl:matching-substring>
            <xsl:value-of select="string-join(for $group in 1 to $size return regex-group($group), ' ')"/>
        </xsl:matching-substring>
    </xsl:analyze-string>
</xsl:template>

OTHER TIPS

Or you could do

<xsl:for-each-group select="string-to-codepoints($in)" 
                    group-adjacent="(position()-1) idiv 6">
  <xsl:value-of select="codepoints-to-string((current-group(), 20))"/>
</xsl:for-each-group>

An alternative solution that uses substring() inside of a for expression to produce the sequence of number groups, then use string-join() with a space separator to produce the desired output:

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

<xsl:template match="item">
 <xsl:variable name="size" select="6"/>
 <xsl:value-of select="string-join(for $i in 0 to string-length(@effrg) div $size
                                     return substring(@effrg, $i*$size+1, $size),
                                   ' ')"/>
</xsl:template>

</xsl:stylesheet>

Since the user asked in a comment about Internet Explorer support, XSLT 2.0 might have been the wrong choice.

Here's a XSLT 1.0 solution:

<xsl:template name="splitString">
    <xsl:param name="string"/>
    <xsl:param name="size"/>
    <xsl:choose>
        <xsl:when test="string-length($string) > $size">
            <xsl:variable name="rest">
                <xsl:call-template name="splitString">
                    <xsl:with-param name="string" select="substring($string,$size + 1)"/>
                    <xsl:with-param name="size" select="$size"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of select="concat(substring($string,1,$size),' ',$rest)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$string"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Can be called like this:

<xsl:call-template name="splitString">
    <xsl:with-param name="string" select="item/@effrg"/>
    <xsl:with-param name="size">6</xsl:with-param>
</xsl:call-template>

Also with analyze-string in XSLT-2.0, actually this would've been sufficient:

<xsl:analyze-string select="item/@effrg" regex=".{{6}}">
    <xsl:matching-substring><xsl:value-of select=".,''"/></xsl:matching-substring>
</xsl:analyze-string>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top