xmlconcatenationxslt-2.0ends-with

how to change value of xml field based on condition with XSLT 2.0


I'm trying to change the value of an specific XML field using XSLT 2.0 if the value ends with "i". The change consists of adding a "0" (zero) before the value.

XML field of interst would be '/message/object/struct/substruct2/ss2field2'. Change should occur for every 'ssfield2' ending with "i", in every 'object'.

The source XML is as follows:

<message>
    <object>
        <meta>
            <field>text</field>
        </meta>
        <struct>
            <sfield1>text</sfield1>
            <sfield2>text</sfield2>
            <substruct1>
                <ss1field>text</ss1field>
            </substruct1>
            <substruct2>
                <ss2field1>text</ss2field1>
                <ss2field2>0001</ss2field2>
            </substruct2>
            <substruct2>
                <ss2field1>text</ss2field1>
                <ss2field2>002i</ss2field2>
            </substruct2>
            <substruct2>
                <ss2field1>text</ss2field1>
                <ss2field2>0003</ss2field2>
            </substruct2>
        </struct>
    </object>
    <object>
        <meta>
            <field>text</field>
        </meta>
        <struct>
            <sfield1>text</sfield1>
            <sfield2>text</sfield2>
            <substruct1>
                <ss1field>text</ss1field>
            </substruct1>
            <substruct2>
                <ss2field1>text</ss2field1>
                <ss2field2>004i</ss2field2>
            </substruct2>
            <substruct2>
                <ss2field1>text</ss2field1>
                <ss2field2>0005</ss2field2>
            </substruct2>
        </struct>
    </object>
</message>

Based in this other post: Xslt change element value based on when condition rule with reference to another element

I tried to build my own stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <xsl:variable name="condition">
        <xsl:for-each select="//substruct2">
            <xsl:choose>
                <xsl:when test="ends-with(ss2field2,'i')">
                    <xsl:value-of select="concat('0',ss2field2)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="ss2field2"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
    </xsl:variable>
    <xsl:template name="value-to-replace">
        <xsl:param name="param.str"/>
        <xsl:param name="param.target"/>
        <xsl:param name="param.replacement"/>
        <xsl:choose>
            <xsl:when test="contains($param.str, $param.target)">
                <xsl:value-of select="concat(substring-before($param.str, $param.target), $param.replacement, substring-after($param.str, $param.target))"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$param.str"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="ss2field2[parent::substruct2]">
        <xsl:element name="{name()}">
            <xsl:call-template name="value-to-replace">
                <xsl:with-param name="param.str" select="."/>
                <xsl:with-param name="param.target" select="//substruct2/ss2field2"/>
                <xsl:with-param name="param.replacement" select="$condition"/>
            </xsl:call-template>
        </xsl:element>
    </xsl:template>
    <!--copy all nodes-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

But it does not work, I'm getting the following error: A sequence of more than one item is not allowed as the second argument of contains() ("0001", "002i", ...)

Any help would be very much appreciated.


Solution

  • Nevermind, I was over complicating the solution, the following XSLT did the trick:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="//ss2field2">
            <xsl:copy>
                <xsl:choose>
                    <xsl:when test="ends-with(.,'i')">
                        <xsl:value-of select="concat('0', .)"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="."/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    I have now another problem but I will save it for another question.