XSL 1.0 Distinct via Munchian and multiple match modes
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"?>
<开发者_如何学JAVA;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
<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">
<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">
<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>
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>
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>
精彩评论