1

Need sequence number based on the nesting of the element. In below example all the reference elements which is not child of any reference element will have sequence number, any nested reference element should have parent sequence number + decimal + position of current reference element.

Input.xml

    <root>
        <front>
            <reference1 type="ref" href="a.xml">
                <reference1 type="ref" href="x.xml"/>
                <reference1 type="ref" href="z.xml"/>
            </reference>
        </front>
        <reference2 type="ref" href="b.xml"/>
        <reference2 type="ref" href="c.xml">
            <reference2 type="ref" href="d.xml">
                <reference2 type="ref" href="y.xml"/>
            </reference>
        </reference>
        <back>
            <reference3 type="ref" href="e.xml"/>
        </back>
    </root>

Output.xml

    <root>
        <reference href="a.xml" sequence="1"/>
        <reference href="x.xml" sequence="1.1"/>
        <reference href="z.xml" sequence="1.2"/>
        <reference href="b.xml" sequence="2"/>
        <reference href="c.xml" sequence="3"/>
        <reference href="d.xml" sequence="3.1"/>
        <reference href="y.xml" sequence="3.1.1"/>
        <reference href="e.xml" sequence="4"/>
    </root>

I am trying this but not able to get the logic:

          <xsl:template match="/">
             <root>
                 <xsl:for-each select="//reference">
                     <reference>
                                 <xsl:attribute name="href">
                                     <xsl:value-of select="@href"/>
                                 </xsl:attribute>
                         <xsl:if test="not(child::reference)">
                                 <xsl:attribute name="sequence">
                                     <xsl:value-of select="position()"/>
                                 </xsl:attribute>
                         </xsl:if>
                         <xsl:if test="child::reference">
                             <!-- something to be done here -->
                         </xsl:if>
                     </reference>    
                 </xsl:for-each>
             </root>

Thanks

Namrata
  • 13
  • 3

1 Answers1

1

My suggestion would be to use <xsl:number level="multiple"/> to do the counting for you. I think that in order for the counts to be correct, you'd have to narrow it down to just reference elements. I might be wrong and there could be an easier way, but I only had a few minutes to spend on this.

In my example, I use a moded template and assigned the cleaned results to a variable. I then apply-templates to that variable to do the actual transform.

This will only work in XSLT 2.0, but since you tagged the question 2.0 you should be ok.

XML Input (added a couple of <foo/> elements for testing)

<root>
    <front>
        <reference href="a.xml">
            <reference href="x.xml"/>
            <reference href="z.xml"/>
        </reference>
    </front>
    <reference href="b.xml"/>
    <foo/>
    <reference href="c.xml">
        <reference href="d.xml">
            <foo>
                <reference href="y.xml"/>                
            </foo>
        </reference>
    </reference>
    <back>
        <reference href="e.xml"/>
    </back>
</root>

XSLT 2.0

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

    <xsl:variable name="refs">
        <refs>
            <xsl:apply-templates select="/*//reference[not(ancestor::reference)]" mode="clean"/>                
        </refs>
    </xsl:variable>

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="$refs"/>            
        </xsl:copy>
    </xsl:template>

    <xsl:template match="reference">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="x">
                <xsl:number level="multiple"/>
            </xsl:attribute>
        </xsl:copy>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="reference" mode="clean">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select=".//reference[ancestor::reference[1] is current()]" mode="clean"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML Output

<root>
   <reference href="a.xml" x="1"/>
   <reference href="x.xml" x="1.1"/>
   <reference href="z.xml" x="1.2"/>
   <reference href="b.xml" x="2"/>
   <reference href="c.xml" x="3"/>
   <reference href="d.xml" x="3.1"/>
   <reference href="y.xml" x="3.1.1"/>
   <reference href="e.xml" x="4"/>
</root>

EDIT

Here's an updated stylesheet for the updated input in the question. I only needed to change reference to *[@type='ref'] and add a count attribute to xsl:number.

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

    <xsl:variable name="refs">
        <refs>
            <xsl:apply-templates select="/*//*[@type='ref'][not(ancestor::*[@type='ref'])]" mode="clean"/>                
        </refs>
    </xsl:variable>

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="$refs"/>            
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@type='ref']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="x">
                <xsl:number count="*[@type='ref']" level="multiple"/>
            </xsl:attribute>
        </xsl:copy>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="*[@type='ref']" mode="clean">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select=".//*[@type='ref'][ancestor::*[@type='ref'][1] is current()]" mode="clean"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • thanks for the solution, your logic works perfect for the previous source. Now I have edited the source, in this new scenario the node name is not always same but type att is same. When i apply your logic, xsl:number start the from scratch for every new reference element, whereas I need same output. Hope you can help. Thanks. – Namrata Dec 11 '14 at 10:19
  • @Namrata - Please see my edit for an updated stylesheet. – Daniel Haley Dec 11 '14 at 14:27