user2842773
user2842773

Reputation: 77

XSL count per occurrence

I want to increment a value in an element based on couple of things. First I want to check for the occurrence of the element value and then change another element value wherever that same ID appears in the node.

This is an example of how the xml file looks:

<?xml version="1.0" encoding="UTF-8" ?>
<XMLLines>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
</XMLLines>

This is the xsl file :

<?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"/> 

    <xsl:template match="*"> 
        <xsl:copy> 
            <xsl:apply-templates select="@*|node()"/> 
        </xsl:copy> 
    </xsl:template> 

    <xsl:key name="TEST1" match="XMLLine[contains(./Type,'STEEL') and (./Value = document('Values.xml')/Values/Value)]" use="ID"/>

    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ARG1[key('TEST1', ../ID)]">
        <xsl:copy>
            <xsl:value-of select="concat('STATUS = ', count(../preceding-sibling::XMLLine[key('TEST1', ID)]) + 1)"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

and this is the xml file that contains values:

<?xml version="1.0" encoding="UTF-8"?>
<Values>
    <Value>"1"</Value>
    <Value>"2"</Value>
    <Value>"3"</Value>
    <Value>"4"</Value>
    <Value>"5"</Value>
    <Value>"6"</Value>
    <Value>"7"</Value>
    <Value>"8"</Value>
</Values>

Right now, the output looks like:

<?xml version="1.0"?>
<XMLLines>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 1</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 2</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 3</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 4</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 5</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 6</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
</XMLLines>

But I actually need it to increment only once per same instead of every time it occurs like in the output above. So basically I would like to have the following output:

<?xml version="1.0"?>
<XMLLines>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 1</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 1</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 2</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 2</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <Value>"1"</Value>
        <ARG1>""</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 3</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <Value>"1"</Value>
        <ARG1>STATUS = 3</ARG1>
        <ARG2>""</ARG2>
        <ARG3>""</ARG3>
    </XMLLine>
</XMLLines>

Any ideas on how to achieve this using XSLT 1.0? Thank you !

Upvotes: 0

Views: 132

Answers (2)

user2842773
user2842773

Reputation: 77

Actually the suggested answer didn't quite help in order to get the intended solution, but I've come to a workaround in order to achieve the needed transformation. Of course this will work only with the current case where there are always two same ID's per matching occurrence.

<?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"/> 

    <xsl:template match="*"> 
        <xsl:copy> 
            <xsl:apply-templates select="@*|node()"/> 
        </xsl:copy> 
    </xsl:template> 

    <xsl:key name="TEST1" match="XMLLine[contains(./Type,'STEEL') and (./Value = document('Values.xml')/Values/Value)]" use="ID"/>

    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ARG1[key('TEST1', ../ID)]">
        <xsl:copy>
            <xsl:value-of select="concat('STATUS = ', round((count(../preceding-sibling::XMLLine[key('TEST1', ID)]) + 1) div 2))"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 117165

Here's one way you could look at this:

XSLT 1.0

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

<xsl:key name="k" match="XMLLine" use="concat(ID, '|', type)"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="ARG1[contains(../Type,'STEEL')]">
    <xsl:copy>
        <xsl:text>STATUS=</xsl:text>
        <xsl:value-of select="count((..|../preceding-sibling::XMLLine)[contains(Type,'STEEL')][count(. | key('k', concat(ID, '|', type))[1]) = 1])" />
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Applied to the following test input:

<XMLLines>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"100"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"103"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"143"</ID>
        <Type>"IRON"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
    <XMLLine>
        <ID>"187"</ID>
        <Type>"STEEL"</Type>
        <ARG1>""</ARG1>
    </XMLLine>
</XMLLines>

the result is:

<?xml version="1.0" encoding="UTF-8"?>
<XMLLines>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=1</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=1</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=1</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"103"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=2</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"103"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=2</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"143"</ID>
      <Type>"IRON"</Type>
      <ARG1>""</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"143"</ID>
      <Type>"IRON"</Type>
      <ARG1>""</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"187"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=3</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"187"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=3</ARG1>
   </XMLLine>
</XMLLines>

I am not sure why a separate document listing a few incrementing numbers would be required.


Although the above is already simpler than your attempt, it could be made even simpler by assigning a unique id to each group, instead of incrementing the number (which requires counting the previous groups). For example:

XSLT 1.0

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

<xsl:key name="k" match="XMLLine" use="concat(ID, '|', type)"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="ARG1[contains(../Type,'STEEL')]">
    <xsl:copy>
        <xsl:text>STATUS=</xsl:text>
        <xsl:value-of select="generate-id(key('k', concat(../ID, '|', ../type))[1])" />
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

would return something like:

<?xml version="1.0" encoding="UTF-8"?>
<XMLLines>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e2</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e2</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"100"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e2</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"103"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e23</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"103"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e23</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"143"</ID>
      <Type>"IRON"</Type>
      <ARG1>""</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"143"</ID>
      <Type>"IRON"</Type>
      <ARG1>""</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"187"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e51</ARG1>
   </XMLLine>
   <XMLLine>
      <ID>"187"</ID>
      <Type>"STEEL"</Type>
      <ARG1>STATUS=d0e51</ARG1>
   </XMLLine>
</XMLLines>

Upvotes: 1

Related Questions