Reputation: 3
I have searched through all the xslt mapping questions but haven't found a solution to my very specific case yet.
So, suppose this is my source XML:
<Data>
<A val='1'>
<CC>1234</CC>
</A>
<A val='2'>
<CC>1234</CC>
<CC>5678</CC>
</A>
<A val='3'>
<CC>1234</CC>
</A>
<B val='1'>
<CC>5678</CC>
</B>
<B val='2'>
<CC>1234</CC>
<CC>9012</CC>
</B>
</Data>
What I want as result is the following:
<Data>
<A val='1'>
<CC>-1</CC>
</A>
<A val='2'>
<CC>-1</CC>
<CC>-2</CC>
</A>
<A val='3'>
<CC>-1</CC>
</A>
<B val='1'>
<CC>-2</CC>
</B>
<B val='2'>
<CC>-1</CC>
<CC>-3</CC>
</B>
</Data>
If I would code this in some programming language I would first put all the CC-values into a set/collection and then when I know all the possible CC-values I would iterate through the set and replace the value in the XML with a counter that is decreased within the loop. But I have no clue how to do that in XSLT... So in the above example I have the CC-values 1234, 5678 and 9012. When iterating over the values, all CC-values having 1234 should become -1, 5678 ==> -2 and 9012 ==>-3 regardless if CC is under A or B. It would also be ok if 5678 is mapped to -1, 9012 to -2 and 1234 to -3, but all occurences of 1234 must be changed to -3 then and so on. Thanks for any help!
Upvotes: 0
Views: 745
Reputation: 70598
Assuming you can use XSLT 2.0, you can use distinct-values
to get the distinct values of CC
, and then for each CC
you can use index-of
to find it's position in the distinct list and use that to output the number you want.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="distinctCC" select="distinct-values(//CC)" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CC">
<xsl:copy>
<xsl:value-of select="0 - index-of($distinctCC, text())" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And if you can only use XSLT 1.0, you would fall back to using Muenchian Grouping to get distinct values, although finding the index is a little more work too.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="cc" match="CC" use="." />
<xsl:variable name="distinctCC" select="//CC[generate-id() = generate-id(key('cc', .)[1])]" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CC">
<xsl:copy>
<xsl:variable name="current" select="text()" />
<xsl:variable name="pos">
<xsl:for-each select="$distinctCC">
<xsl:if test="$current = ."><xsl:value-of select="position()" /></xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="0 - $pos" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 7173
you can create a "map" by putting all distinct-values in a variable, such as:
<xsl:variable name="CCcoll">
<xsl:for-each select="distinct-values(//CC)">
<CC><xsl:value-of select="."/></CC>
</xsl:for-each>
</xsl:variable>
add an identity template and a template override for CC
nodes:
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CC">
<xsl:variable name="curr_CC" select="."/>
<xsl:copy>
<!-- here, you are matching the
current value to the "map"
and getting it's position -->
<xsl:value-of select="concat('-', $CCcoll/CC[.=$curr_CC]/count(preceding-sibling::*) + 1)"/>
</xsl:copy>
</xsl:template>
The whole stylesheet is below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="CCcoll">
<xsl:for-each select="distinct-values(//CC)">
<CC><xsl:value-of select="."/></CC>
</xsl:for-each>
</xsl:variable>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CC">
<xsl:variable name="curr_CC" select="."/>
<xsl:copy>
<xsl:value-of select="concat('-', $CCcoll/CC[.=$curr_CC]/count(preceding-sibling::*) + 1)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0