Adrian
Adrian

Reputation: 2656

Escape input in XSLT

I have the following XSLT, which formats the output by grouping keys on a given array:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf">


    <xsl:param name="values" select="'Sniper 50 Red', 'Xiper 10 Red'"/>

    <xsl:output indent="yes"/>

    <xsl:function name="mf:match" as="xs:string">
        <xsl:param name="input" as="xs:string"/>
        <xsl:param name="values" as="xs:string*"/>
        <xsl:sequence
            select="if ($values[matches($input, concat('^', .))])
                    then $values[matches($input, concat('^', .))]
                    else $input"/>
    </xsl:function>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="results">
        <xsl:copy>
            <xsl:for-each-group select="pageFunctionResult" group-by="mf:match(product_name, $values)">
                <xsl:choose>
                    <xsl:when test="product_name ne current-grouping-key()">
                            <xsl:copy>
                                <position>MAINPRODUCT</position>
                                <variantname>
                                    <xsl:value-of select="product_name"/>
                                </variantname>
                                <product_namea>
                                    <xsl:value-of select="current-grouping-key()"/>
                                </product_namea>
                                <xsl:copy-of select="category, product_id, quantity, manufacturer,image"/>
                                <xsl:apply-templates select="current-group() except ."/>
                            </xsl:copy>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:for-each select="current-group()">
                        <xsl:copy>
                            <position>MAINPRODUCT</position>
                            <variantname>
                                <xsl:value-of select="current-grouping-key()"/>
                            </variantname>
                            <xsl:apply-templates select="*"/>
                        </xsl:copy>
                        </xsl:for-each>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="pageFunctionResult">
        <variant id="{position()}">
            <xsl:apply-templates/>
        </variant>
    </xsl:template>

</xsl:transform>

Here you can see it in action. It works well, but I now have keys with bracket in their names:

<xsl:param name="values" select="'Sniper 50 Red (26)', 'Xiper 10 Red (27)'"/>

...and this obviously brakes the code, because brackets are reserved in regex. So the brackets should be escaped. I got a tip to use functx:escape-for-regex, but I am unable to implement it to the code.

Upvotes: 0

Views: 306

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167426

This example shows how to use the functx function:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    xmlns:functx="http://www.functx.com"
    exclude-result-prefixes="xs mf functx">

    <xsl:param name="values" select="'Sniper 50 Red (26)', 'Xiper 10 Red (27)'"/>

    <xsl:output indent="yes"/>

    <xsl:function name="functx:escape-for-regex" as="xs:string"
        >
        <xsl:param name="arg" as="xs:string?"/>

        <xsl:sequence select="
            replace($arg,
            '(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
            "/>

    </xsl:function>

    <xsl:function name="mf:match" as="xs:string">
        <xsl:param name="input" as="xs:string"/>
        <xsl:param name="values" as="xs:string*"/>
        <xsl:sequence
            select="if ($values[matches($input, concat('^', functx:escape-for-regex(.)))])
            then $values[matches($input, concat('^', functx:escape-for-regex(.)))]
            else $input"/>
    </xsl:function>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="results">
        <xsl:copy>
            <xsl:for-each-group select="pageFunctionResult" group-by="mf:match(product_name, $values)">
                <xsl:choose>
                    <xsl:when test="product_name ne current-grouping-key()">
                        <xsl:copy>
                            <position>MAINPRODUCT</position>
                            <variantname>
                                <xsl:value-of select="product_name"/>
                            </variantname>
                            <product_namea>
                                <xsl:value-of select="current-grouping-key()"/>
                            </product_namea>
                            <xsl:copy-of select="category, product_id, quantity, manufacturer,image"/>
                            <xsl:apply-templates select="current-group() except ."/>
                        </xsl:copy>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:for-each select="current-group()">
                            <xsl:copy>
                                <position>MAINPRODUCT</position>
                                <variantname>
                                    <xsl:value-of select="current-grouping-key()"/>
                                </variantname>
                                <xsl:apply-templates select="*"/>
                            </xsl:copy>
                        </xsl:for-each>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="pageFunctionResult">
        <variant id="{position()}">
            <xsl:apply-templates/>
        </variant>
    </xsl:template>

</xsl:transform>

Upvotes: 1

Related Questions