1

I have an xml doc :

<xml>
   <staff>
       <seq_no num="0">0</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
       <seq_no num="1">1</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="1">2</seq_no>
      <name>abc</name>
   </staff>
   <staff>
      <seq_no num="3">3</seq_no>
      <name>abc</name>
   </staff>
 </xml>

I want to add number to seq_no/@num if any of the seq_no are same. if there is no duplicate then nothing will get added.

The out put of above should be:

    <xml>
     <staff>  
          <seq_no num="[1]1">0</seq_no>
          <name>xyz</name>
       </staff>
     <staff>
          <seq_no num="[2]1">1</seq_no>
          <name>xyz</name>
     </staff>
     <staff>
         <seq_no num="[3]1">2</seq_no>
         <name>abc</name>
      </staff>
     <staff>
         <seq_no num="[4]3">3</seq_no>
         <name>abc</name>
      </staff>
</xml>

added [1],[2],[3],[4] respectively to seq_no/@num. I started the work with match template like this

<xsl:template match="seq_no/num">
<seq_no>
  <xsl:attribute name="num">
    <xsl:text>[</xsl:text><xsl:number select="." format="1" level="any"  /><xsl:text>]</xsl:text>
    <xsl:value-of select="@num"></xsl:value-of>
  </xsl:attribute>
<xsl:value-of select="."></xsl:value-of>
</seq_no>
</xsl:template>

but getting trouble when controling the postion as well as how to check the duplicate num for other seq_no...

Input without any duplicate num:

 <xml>
   <staff>
       <seq_no num="0">0</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
       <seq_no num="1">1</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="2">2</seq_no>
      <name>abc</name>
   </staff>
   <staff>
      <seq_no num="3">3</seq_no>
      <name>abc</name>
   </staff>
 </xml>.

Output should be same as input no processing requried.

 <xml>
   <staff>
       <seq_no num="0">0</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
       <seq_no num="1">1</seq_no>
       <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="1">2</seq_no>
      <name>abc</name>
   </staff>
   <staff>
      <seq_no num="3">3</seq_no>
      <name>abc</name>
   </staff>
 </xml>
2
  • is your output example above correct? The [x] gets added all the time - when should it not be added? And why does the first stuff not have a seq_no/@num ? Commented Aug 28, 2012 at 20:34
  • if there is no duplicate value in seq_no/@num then it will not add. so if in the above xml input if I change<seq_no num="1">2</seq_no> to <seq_no num="2">2</seq_no> then nothing will get added. we have to apply this logic only if there is attribute seq_no/@num present other wise just ignore that node. Commented Aug 28, 2012 at 21:10

2 Answers 2

1

This transformation:

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

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

 <xsl:template match="seq_no[/*/*/seq_no[@num = following::seq_no/@num]]">
  <seq_no num="[{count(preceding::seq_no)+1}]{.}">
   <xsl:apply-templates/>
  </seq_no>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<xml>
    <staff>
        <seq_no num="0">0</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="1">1</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="1">2</seq_no>
        <name>abc</name>
    </staff>
    <staff>
        <seq_no num="3">3</seq_no>
        <name>abc</name>
    </staff>
</xml>

produces the wanted, correct result:

<xml>
   <staff>
      <seq_no num="[1]0">0</seq_no>
      <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="[2]1">1</seq_no>
      <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="[3]2">2</seq_no>
      <name>abc</name>
   </staff>
   <staff>
      <seq_no num="[4]3">3</seq_no>
      <name>abc</name>
   </staff>
</xml>

When the same transformation (above) is applied on this XML document (without @num diplicates):

<xml>
    <staff>
        <seq_no num="0">0</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="1">1</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="2">2</seq_no>
        <name>abc</name>
    </staff>
    <staff>
        <seq_no num="3">3</seq_no>
        <name>abc</name>
    </staff>
</xml>

again the wanted, correct result is produced:

<xml>
  <staff>
    <seq_no num="0">0</seq_no>
    <name>xyz</name>
  </staff>
  <staff>
    <seq_no num="1">1</seq_no>
    <name>xyz</name>
  </staff>
  <staff>
    <seq_no num="2">2</seq_no>
    <name>abc</name>
  </staff>
  <staff>
    <seq_no num="3">3</seq_no>
    <name>abc</name>
  </staff>
</xml>
Sign up to request clarification or add additional context in comments.

5 Comments

Hi Dimitre, you missed the duplicate check, so if any @num is duplicated then nothing will get inserted.
@atif, Please explain??? Nothing is lost and all duplicate numbers are represented in the result of this solution. Maybe you need to edit the question and add there any new, additional requirements, together with an appropriate example.
i have updated the question with input and output when no duplicate value of "@num" attribute. bascailly if any "@num" value is duplicated then only it will insert the [1],[2] otherwise no processing needed i-e no change in output. Output and input should be same.
@atif, OK now it is more understandable. Please, see my updated answer.
Hi Dimrite, if i have to change this to match sec_no text() instead of num attribute, what should i change...i tried this match="seq_no[/*/*/seq_no[text() = following::seq_no/text()]]"> but it didn't work...
1

The logic still doesn't seem to match your input/output examples, but here's what I think you're trying to do...

XML Input

<xml>
    <staff>
        <seq_no num="0">0</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="1">1</seq_no>
        <name>xyz</name>
    </staff>
    <staff>
        <seq_no num="1">2</seq_no>
        <name>abc</name>
    </staff>
    <staff>
        <seq_no num="3">3</seq_no>
        <name>abc</name>
    </staff>
</xml>

XSLT 1.0

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

    <!--Check for any duplicates in the doc.-->
    <xsl:variable name="vDups" select="boolean(//seq_no[@num=preceding::seq_no/@num])"/>

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

    <xsl:template match="seq_no">
        <xsl:copy>
            <xsl:choose>
                <xsl:when test="$vDups">
                    <xsl:attribute name="num">
                        <xsl:text>[</xsl:text>
                        <xsl:number level="any"/>
                        <xsl:text>]</xsl:text>
                        <xsl:value-of select="."/>                      
                    </xsl:attribute>
                    <xsl:apply-templates select="@*[not(name()='num')]|node()"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML Output

<xml>
   <staff>
      <seq_no num="[1]0">0</seq_no>
      <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="[2]1">1</seq_no>
      <name>xyz</name>
   </staff>
   <staff>
      <seq_no num="[3]2">2</seq_no>
      <name>abc</name>
   </staff>
   <staff>
      <seq_no num="[4]3">3</seq_no>
      <name>abc</name>
   </staff>
</xml>

Optional, XSLT 2.0 template replacement that's cleaner:

<xsl:template match="seq_no[$vDups]">
    <xsl:copy>
        <xsl:attribute name="num">
            <xsl:text>[</xsl:text>
            <xsl:number level="any"/>
            <xsl:text>]</xsl:text>
            <xsl:value-of select="."/>                      
        </xsl:attribute>
        <xsl:apply-templates select="@*[not(name()='num')]|node()"/>
    </xsl:copy>
</xsl:template>

3 Comments

Hi Dev, <xsl:variable name="vDups" select="boolean(//seq_no[.=@num])"/> always return true regardless of if we don't have any duplicate num.
@atif - That's because my understanding of a duplicate was wrong. Now that you've update your question with an example of no duplicates, I can see what the logic is. I've updated the xsl:variable in my answer. Please try it again.
Thank you @devnull, it worked although i have already implemented Dimitre logic but did try your updated code and it is working fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.