Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT: How to get position index of particular nodes in all the childs combined

Tags:

xml

xslt

xpath

Consider the following XML:

<root>
    <a>
        <pos>
            <c val="abc"></c>
            <c val="def"></c>
            <c val="ghi"></c>
            <c val="jkl"></c>
            <c val="mno"></c>
        </pos>
        <b>
            <c></c>
        </b>
        <b>
            <c></c>
            <d>here</d>
        </b>
        <b>
            <e>and</e>
            <c></c>
            <d>for</d>
        </b>
        <b>
            <c></c>
            <c></c>
            <d>also</d>
        </b>
    </a>
    <a>
        <pos>
            <c val="pqr"></c>
            <c val="stu"></c>
            <c val="vwx"></c>
            <c val="yz"></c>
        </pos>
        <b>
            <c></c>
            <d>what</d>
        </b>
        <b>
            <c></c>
        </b>
        <b>
            <d>how</d>
        </b>
        <b>
            <c></c>
            <d>where</d>
            <c></c>
        </b>
    </a>
</root>

Now in my output, when ever I encounter a <c></c> inside a b, I need to put the corresponding value of the val attribute of a <c></c> from the <pos></pos> node. By corresponding value I mean the index of node c inside pos should be the same as the index of c in all the b nodes combined.

The desired output is:

<start>
    <level>
        abc
    </level>
    <level>
        def
        here
    </level>
    <level>
        and
        ghi
        for
    </level>
    <level>
        jkl
        mno
        also
    </level>
<start>


<start>
    <level>
        pqr
        what
    </level>
    <level>
        stu
    </level>
    <level>
        how
    </level>
    <level>
        vwx
        where
        yz
    </level>
 </start>

I attempted it with the following XSL:

<xsl:template match="root">
    <star>
        <xsl:apply-templates select="a"/>
    </start>
</xsl:template>
<xsl:template match="a">
    <xsl:apply-templates select="b"/>
</xsl:template>
<xsl:template match="b">
    <level>
      <xsl:for-each select="*">
          <xsl:choose>
            <xsl:when test="name() = 'd'">
                <xsl:value-of select="."/>
            </xsl:when>
            <xsl:when test="name() = 'e'">
                <xsl:value-of select="."/>
            </xsl:when>
            <xsl:when test="name() = 'c'">
                <xsl:variable name="posCount">
                    <!-- I don't know what to do here -->
                </xsl:valiable>
                <xsl:for-each select="ancestor::a[1]/pos">
                    <xsl:for-each select="c[position() = $posCount]">
                          <xsl:value-of select="@val"/>
                    </xsl:for-each>
                </xsl:for-each>

            </xsl:when>
          </xsl:choose>
      </xsl:for-each>
    </level>
</xsl:template>

What I need to do is somehow get the position count of each c inside all the bs combined and then use the value of val attribute of correspondingly positioned c from inside pos.

How can I proceed to do it?

Note: I am using XSLT 1.0

Thnx in advance!!

like image 478
Surender Thakran Avatar asked Mar 21 '23 09:03

Surender Thakran


1 Answers

The index number you are looking for can be obtained quite easily by using the xsl:number element. For example:

<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:template match="root">
    <root>
        <xsl:apply-templates select="a"/>
    </root>
</xsl:template>

<xsl:template match="a">
    <start>
        <xsl:apply-templates select="b"/>
    </start>
</xsl:template>

<xsl:template match="b">
    <level>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="*"/>
    </level>
</xsl:template>

<xsl:template match="d|e">
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>

<xsl:template match="b/c">
<xsl:variable name="i">
    <xsl:number count="b/c" from="a" level="any" />
</xsl:variable>
    <xsl:value-of select="ancestor::a/pos/c[number($i)]/@val"/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>

</xsl:stylesheet>

Notes:
1. I have added a root element to the output to make it a valid XML;
2. It would be best to use a key to get the corresponding value from pos/c.

like image 84
michael.hor257k Avatar answered Apr 26 '23 13:04

michael.hor257k