Search code examples
xsltxslt-1.0xslkey

XSL group by node value


I have a case of XSL key grouping. The goal is to replace a value based on an ID match.

Input:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<message-in>
    <actions>
        <stop>
            <action id="33632">
                <text>DefaultComment</text>
                <planning-status>finished</planning-status>
                <realised-times>
                    <starttime>2017-03-13T12:43:54</starttime>
                    <finishtime>2017-03-13T13:15:21</finishtime>
                </realised-times>
            </action>
        </stop>
        <stop>
            <action id="33635">
                <planning-status>started</planning-status>
                <realised-times>
                    <starttime>2017-03-13T13:15:21</starttime>
                </realised-times>
            </action>
        </stop>
    </actions>
</message-in>
<output_getquerydata>
    <queries>
        <query name="fat">
            <parameters>
                <parameter name="id">33632</parameter>
            </parameters>
            <queryErrors/>
            <queryResults>
                <record id="1">
                    <column name="interfaceAction_externalId">33633OREA</column>
                </record>
            </queryResults>
        </query>
    </queries>
</output_getquerydata>
<output_getquerydata>
    <queries>
        <query name="fat">
            <parameters>
                <parameter name="id">33635</parameter>
            </parameters>
            <queryErrors/>
            <queryResults>
                <record id="1">
                    <column name="interfaceAction_externalId">536313OREA</column>
                </record>
            </queryResults>
        </query>
    </queries>
</output_getquerydata>
</root>

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:key name="actionKey" match="stop" use="action/@id"/>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="/">
    <root>
        <xsl:copy-of select="//message-in"/>
    </root>
</xsl:template>
<xsl:template match="action/@id">
    <xsl:attribute name="id"><xsl:value-of select="substring-before(//output_getquerydata/queries/query/parameters/parameter[key('actionKey', @id)]/queryResults/record/column[@name='interfaceAction_externalId'],'OREA')"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>

I have multiple "action" nodes that have matching "query" nodes. The goal is that for every action ID we need to replace the ID value from the "action" tag with the corresponding "interfaceAction_externalId" value. So for action ID 33632 we'll copy and replace with the value of "33633" (because we have a match in parameneter/name/@id 33632 = action/@id ).

The copy works well I get all the information I need, but it seems that action/@id doesn't get replaced. I thought that I'll use a key to save the values of action/@id then use that in the template match to replace with the value from interfaceAction_externalId, but it doesn't work and I'm not sure what I'm doing wrong.

Thanks for your help!


Solution

  • The whole approach only makes sense if you use <xsl:apply-templates select="//message-in"/> instead of <xsl:copy-of select="//message-in"/>.

    As for using a key, I think you want

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
        <xsl:strip-space elements="*"/>
        <xsl:key name="ref-query"
            match="output_getquerydata/queries/query/queryResults/record/column[@name='interfaceAction_externalId']"
            use="ancestor::query/parameters/parameter[@name = 'id']"/>
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="/">
            <root>
                <xsl:apply-templates select="//message-in"/>
            </root>
        </xsl:template>
        <xsl:template match="action/@id">
            <xsl:attribute name="id"><xsl:value-of select="substring-before(key('ref-query', .),'OREA')"/></xsl:attribute>
        </xsl:template>
    </xsl:stylesheet>