简体   繁体   中英

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 !

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>

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM