tags:

views:

26

answers:

2

Learning XSL, relegated at this time to XSL 1.0, trying to produce a distinct set as well perform subsequent pass using match modes constructs.

If the modes are not introduced all is well.

As soon as modes are introduced the translation fails.

Any help would be appreciated.

Exempliary xml:

    <?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <node>
        <category comat="0" catat="AC1" catatt="AD1">C1</category>
        <desc>D1</desc>
    </node>
    <node>
        <category comat="0" catat="AC2" catatt="AD2">C2</category>
        <desc>D2</desc>
    </node>
    <node>
        <category comat="0" catat="AC1" catatt="AD1">C1</category>
        <desc>D1</desc>
    </node>
    <node>
        <category comat="0" catat="AC3" catatt="AD3">C3</category>
        <desc>D3</desc>
    </node>
</dataset>

Sample Munchian producing distinct records.

    <?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common"&gt;
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/><!-- New document created with EditiX at Thu Aug 05 16:51:01 PDT 2010 -->
    <xsl:key name="nodekey" match="node/category" use="concat(@catat,'_',@catatt)"/>
    <xsl:template match="dataset">
        <CAT>
            <xsl:apply-templates />
        </CAT>
    </xsl:template>
    <xsl:template match="dataset" >
        <CATD>
            <xsl:for-each select="node/category[generate-id()= generate-id(key('nodekey',concat(@catat,'_',@catatt))[1])]">
                <CAT_D>
                    <xsl:value-of select="concat(@catat,'_',@catatt)"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="."/>
                </CAT_D>
            </xsl:for-each>
        </CATD>
    </xsl:template>
</xsl:stylesheet>

Sample Output:

<?xml version="1.0" encoding="UTF-8"?>
<CATD xmlns:exslt="http://exslt.org/common"&gt;
   <CAT_D>AC1_AD1_C1</CAT_D>
   <CAT_D>AC2_AD2_C2</CAT_D>
   <CAT_D>AC3_AD3_C3</CAT_D>
</CATD>

However, when modes are added the same translations fail?

    <?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common"&gt;
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/><!-- New document created with EditiX at Thu Aug 05 16:51:01 PDT 2010 -->
    <xsl:key name="nodekey" match="node/category" use="concat(@catat,'_',@catatt)"/>
    <xsl:template match="dataset">
        <UCAT>
            <xsl:apply-templates mode="ucatmode"/>
        </UCAT>
        <DCAT>
            <xsl:apply-templates mode="catmode"/>
        </DCAT>
    </xsl:template>
    <xsl:template match="dataset" mode="ucatmode">
        <DESC>
            <xsl:for-each select="node/category[generate-id()= generate-id(key('nodekey',concat(@catat,'_',@catatt))[1])]">
                <CAT_D>
                    <xsl:value-of select="concat(@catat,'_',@catatt)"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="."/>
                </CAT_D>
            </xsl:for-each>
        </DESC>
    </xsl:template>
    <xsl:template match="dataset/node/desc" mode="catmode">
        <CATQ>
            <xsl:value-of select="."/>
        </CATQ>
    </xsl:template>
</xsl:stylesheet>
+3  A: 

However, when modes are added the same translations fail?

You problem is in this code:

<xsl:template match="dataset">
    <UCAT>
        <xsl:apply-templates mode="ucatmode"/>
    </UCAT>
    <DCAT>
        <xsl:apply-templates mode="catmode"/>
    </DCAT>
</xsl:template>

<xsl:template match="dataset" mode="ucatmode">

The instruction above:

<xsl:apply-templates mode="ucatmode"/>

is a shorthand of;

<xsl:apply-templates select="child::node()"
     mode="ucatmode"/>

However the children of the dataset element are only elements named node and white-space-only text nodes. There isn't any template in your code that is in a mode named ucatmode and that matches a node element.

Therefore, no template is selected for processing and the XSLT processor uses the built-in templates (they are available for any mode). The XSLT built-in templates cause all text nodes to be copied -- and this is what you get.

Exactly the same problem exists with the instruction:

<xsl:apply-templates mode="catmode"/>

Solution: Replace:

<xsl:template match="dataset">
    <UCAT>
        <xsl:apply-templates mode="ucatmode"/>
    </UCAT>
    <DCAT>
        <xsl:apply-templates mode="catmode"/>
    </DCAT>
</xsl:template>

with

<xsl:template match="/">
    <UCAT>
        <xsl:apply-templates mode="ucatmode"/>
    </UCAT>
    <DCAT>
        <xsl:apply-templates mode="catmode"/>
    </DCAT>
</xsl:template>

This template now matches the document node, its only (top) child is the dataset element and the two moded templates matching dataset will now be selected for processing.

You still need to replace:

<xsl:apply-templates mode="catmode"/>

with:

    <xsl:apply-templates mode="catmode"
         select="dataset/node/desc"/>

because desc is not a child of dataset and built-in template processing will be involved again for all descendents of dataset, with the exception of desc.

One minor additional correction is to eliminate all white-space-only text nodes -- this can conveniently be achieved by the following XSLT instruction:

<xsl:strip-space elements="*"/>

Yet another minor improvement is to prevent the exslt: prefix from appearing with every literal-result element. This is achieved by adding the attribute:

exclude-result-prefixes="exslt"

to the <xsl:stylesheet> instruction.

The complete corrected transformation (properly indented for readability) thus is:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:exslt="http://exslt.org/common"
 exclude-result-prefixes="exslt"
 >
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="nodekey" match="node/category"
    use="concat(@catat,'_',@catatt)"/>

    <xsl:template match="/">
        <UCAT>
            <xsl:apply-templates mode="ucatmode"/>
        </UCAT>
        <DCAT>
            <xsl:apply-templates mode="catmode"
                 select="dataset/node/desc"/>
        </DCAT>
    </xsl:template>

    <xsl:template match="dataset" mode="ucatmode">
        <DESC>
            <xsl:for-each select=
             "node/category
                   [generate-id()
                   =
                    generate-id(key('nodekey',
                                     concat(@catat,'_',@catatt)
                                     )[1]
                                )
                    ]">
                <CAT_D>
                    <xsl:value-of select="concat(@catat,'_',@catatt)"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="."/>
                </CAT_D>
            </xsl:for-each>
        </DESC>
    </xsl:template>

    <xsl:template match="dataset/node/desc" mode="catmode">
        <CATQ>
            <xsl:value-of select="."/>
        </CATQ>
    </xsl:template>
</xsl:stylesheet>

When it is applied on the provided XML document:

<dataset>
    <node>
        <category comat="0" catat="AC1" catatt="AD1">C1</category>
        <desc>D1</desc>
    </node>
    <node>
        <category comat="0" catat="AC2" catatt="AD2">C2</category>
        <desc>D2</desc>
    </node>
    <node>
        <category comat="0" catat="AC1" catatt="AD1">C1</category>
        <desc>D1</desc>
    </node>
    <node>
        <category comat="0" catat="AC3" catatt="AD3">C3</category>
        <desc>D3</desc>
    </node>
</dataset>

the wanted, correct result is produced:

<UCAT>
   <DESC>
      <CAT_D>AC1_AD1_C1</CAT_D>
      <CAT_D>AC2_AD2_C2</CAT_D>
      <CAT_D>AC3_AD3_C3</CAT_D>
   </DESC>
</UCAT>
<DCAT>
   <CATQ>D1</CATQ>
   <CATQ>D2</CATQ>
   <CATQ>D1</CATQ>
   <CATQ>D3</CATQ>
</DCAT>
Dimitre Novatchev
@Dimitre: +1 Good and extend answer!
Alejandro
Great, well done, thanks, preliminary testing shows working.Will now incorporate into the formal integration, this was a simplified example for forums purposes, and see if this approach can be adopted into the more formal final solutoon.Again, thanks Dimitre.
@user412632: If you really found my answer useful, the established way to express this in SO is to accept the answer -- just click on the check-mark sign near the answer. :)
Dimitre Novatchev
A: 

Thanks Dimitre, your suggestions worked as desribed.

Adding formal solution for reference.

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" exclude-result-prefixes="exslt" >
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="distinct-CompositeKeyNOS" match="Downloads/Download[@ParentDownloadID='0']" use="concat(@Version,'_',@DownloadType,'_',@Name,'_',@IsBeta)"/>
<xsl:template match="/">
<NotifiusExportData>

            <xsl:apply-templates mode="OSAgnostic">
    </xsl:apply-templates> 

            <xsl:apply-templates mode="english">
    </xsl:apply-templates> 

            <xsl:apply-templates mode="DevicesLangCodes">
    </xsl:apply-templates> 
</NotifiusExportData>
</xsl:template>



    <xsl:template match="NotifiusExportData" mode="OSAgnostic">

        <xsl:for-each select="Downloads/Download[generate-id()= generate-id(key('distinct-CompositeKeyNOS',concat(@Version,'_',@DownloadType,'_',@Name,'_',@IsBeta))[1])]">
                <xsl:sort select="@Version" order="descending" data-type="number"/>
                <xsl:sort select="@DownloadType" order="ascending"/>
                <xsl:sort select="@Name" order="ascending"/>
                <xsl:sort select="@OS" order="descending" data-type="number"/>
                <xsl:sort select="@Is64Bit" order="descending" data-type="number"/>
                <xsl:sort select="@IsBeta" order="ascending" data-type="number"/>
            <Downloads>
                <CompositeKeyNOS>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </CompositeKeyNOS>
                <CompositeKey>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Is64Bit"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </CompositeKey>
                <ParentDownloadID>
                    <xsl:value-of select="@ParentDownloadID"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </ParentDownloadID>
                <DownloadId>
                    <xsl:value-of select="@DownloadId"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </DownloadId>
                <Release>
                    <xsl:value-of select="@Release"/>
                </Release>
                <Version>
                    <xsl:value-of select="@Version"/>
                </Version>
                <DetailsURL>
                    <xsl:value-of select="@DetailsURL"/>
                </DetailsURL>
                <IsBeta>
                    <xsl:value-of select="@IsBeta"/>
                </IsBeta>
                <ReleaseDate>
                    <xsl:value-of select="@ReleaseDate"/>
                </ReleaseDate>
                <DownloadType>
                    <xsl:value-of select="@DownloadType"/>
                </DownloadType>
                <OS>
                    <xsl:value-of select="@OS"/>
                </OS>
                <Is64Bit>
                    <xsl:value-of select="@Is64Bit"/>
                </Is64Bit>
                <LangCode5>
                    <xsl:value-of select="@LangCode5"/>
                </LangCode5>
                <Name>
                    <xsl:value-of select="@Name"/>
                </Name>
                <GraphicsVersion>
                    <xsl:value-of select="@GraphicsVersion"/>
                </GraphicsVersion>
                <USBEmitterVersion>
                    <xsl:value-of select="@USBEmitterVersion"/>
                </USBEmitterVersion>
            </Downloads>

        </xsl:for-each>

    </xsl:template>


    <xsl:template match="NotifiusExportData/Downloads" mode="english"><!-- xsl:for-each select="." --><!--xsl:if test="@ParentDownloadID=0"-->
        <xsl:for-each select="Download[@ParentDownloadID='0']">
            <xsl:sort select="@Version" order="descending" data-type="number"/>
            <xsl:sort select="@DownloadType" order="ascending"/>
            <xsl:sort select="@Name" order="ascending"/>
            <xsl:sort select="@OS" order="descending" data-type="number"/>
            <xsl:sort select="@Is64Bit" order="descending" data-type="number"/>
            <xsl:sort select="@IsBeta" order="ascending" data-type="number"/>
            <NDownloads>
                <NCompositeKeyNOS>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </NCompositeKeyNOS>
                <NCompositeKey>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Is64Bit"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </NCompositeKey>
                <ParentDownloadID>
                    <xsl:value-of select="@ParentDownloadID"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </ParentDownloadID>
                <DownloadId>
                    <xsl:value-of select="@DownloadId"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </DownloadId>
                <Release>
                    <xsl:value-of select="@Release"/>
                </Release>
                <Version>
                    <xsl:value-of select="@Version"/>
                </Version>
                <DetailsURL>
                    <xsl:value-of select="@DetailsURL"/>
                </DetailsURL>
                <IsBeta>
                    <xsl:value-of select="@IsBeta"/>
                </IsBeta>
                <ReleaseDate>
                    <xsl:value-of select="@ReleaseDate"/>
                </ReleaseDate>
                <DownloadType>
                    <xsl:value-of select="@DownloadType"/>
                </DownloadType>
                <OS>
                    <xsl:value-of select="@OS"/>
                </OS>
                <Is64Bit>
                    <xsl:value-of select="@Is64Bit"/>
                </Is64Bit>
                <LangCode5>
                    <xsl:value-of select="@LangCode5"/>
                </LangCode5>
                <Name>
                    <xsl:value-of select="@Name"/>
                </Name>
                <GraphicsVersion>
                    <xsl:value-of select="@GraphicsVersion"/>
                </GraphicsVersion>
                <USBEmitterVersion>
                    <xsl:value-of select="@USBEmitterVersion"/>
                </USBEmitterVersion>
            </NDownloads><!--/xsl:if --><!-- /xsl:for-each -->
        </xsl:for-each>
    </xsl:template><!-- xsl:template match="Download" mode="DevicesLangCodes" -->



<!-- xsl:template match="Download" mode="DevicesLangCodes" -->

    <xsl:template match="NotifiusExportData/Downloads" mode="DevicesLangCodes"><!-- xsl:for-each select="." --><!-- xsl:if test="@ParentDownloadID!=0" -->
        <xsl:for-each select="Download">
            <xsl:sort select="@Version" order="descending" data-type="number"/>
            <xsl:sort select="@DownloadType" order="ascending"/>
            <xsl:sort select="@Name" order="ascending"/>
            <xsl:sort select="@LangCode5" order="ascending"/>
            <xsl:sort select="@OS" order="descending" data-type="number"/>
            <xsl:sort select="@Is64Bit" order="descending" data-type="number"/>
            <xsl:sort select="@IsBeta" order="ascending" data-type="number"/>
            <NNDownloads>
                <NNCompositeKeyNOS>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </NNCompositeKeyNOS>
                <NNCompositeKey>
                    <xsl:value-of select="@Version"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@DownloadType"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Name"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@Is64Bit"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@IsBeta"/>
                </NNCompositeKey>
                <NParentDownloadID>
                    <xsl:value-of select="@ParentDownloadID"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </NParentDownloadID>
                <NDownloadId>
                    <xsl:value-of select="@DownloadId"/>
                    <xsl:text>_</xsl:text>
                    <xsl:value-of select="@OS"/>
                </NDownloadId>
                <NLangCode5>
                    <xsl:value-of select="@LangCode5"/>
                </NLangCode5>
                <xsl:call-template name="groupLangCodes"/>
                <xsl:call-template name="groupDeviceIds"/>
            </NNDownloads>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="groupLangCodes" match="LangCodes">
        <LangCodes>
            <xsl:call-template name="getLangCodeIds"/>
        </LangCodes>
    </xsl:template>
    <xsl:template name="groupDeviceIds" match="DeviceIds">
        <DeviceIds>
            <xsl:call-template name="getDeviceIds"/>
        </DeviceIds>
    </xsl:template>
    <xsl:template name="getLangCodeIds">
        <xsl:for-each select="LangCodes/LangCode">
            <xsl:value-of select="@Id"/>
            <xsl:text> </xsl:text>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="getDeviceIds">
        <xsl:for-each select="DeviceIds/DeviceId">
            <xsl:value-of select="@Id"/>
            <xsl:text> </xsl:text>
        </xsl:for-each>
    </xsl:template>

  </xsl:stylesheet>