Syam
Syam

Reputation: 13

Xslt for renaming a list

I have this XML:

<Root>
    <Tag>
        <Childlist>
            <I>1</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
        </Childlist>
        <Childlist>
            <I>11</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
        </Childlist>
    </Tag>
</Root>

and I want to convert this to:

<Root>
    <Tag>
        <Childlist>
            <item>
                <I>1</I>
                <Dlist>
                    <item>2</item>
                    <item>3</item>
                    <item>4</item>
                </Dlist>
            </item>
            <item>
                <I>11</I>
                <Dlist>
                    <item>2</item>
                    <item>3</item>
                    <item>4</item>
                </Dlist>
            </item>
        </Childlist>
    </Tag>
</Root>

using XSLT. As you can see the repeating tags in first XML gets replaced with tag and list tag is made as parent tag.

Can anybody please advise an xslt for this conversion?

Thanks in advance.

Edit: From comments.

I have a problem with this xls. If i add any tag below Dlist it gets appended to Dlist

There are going to be other preceding and following elements [from Dlist]

Upvotes: 1

Views: 149

Answers (2)

user357812
user357812

Reputation:

Besides Dimitre's excellent answer, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="Tag">
        <Tag>
            <Childlist>
                <xsl:apply-templates select="node()[1]"/>
            </Childlist>
        </Tag>
    </xsl:template>
    <xsl:template match="*/Dlist[1]">
        <xsl:copy>
            <xsl:call-template name="makeItem"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Childlist|Dlist" name="makeItem">
        <Item>
            <xsl:apply-templates select="node()[1]"/>
        </Item>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
</xsl:stylesheet>

Output:

<Root>
    <Tag>
        <Childlist>
            <Item>
                <I>1</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
            </Item>
            <Item>
                <I>11</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
            </Item>
        </Childlist>
    </Tag>
</Root>

Edit: With this input:

<Root>
    <Tag>
        <Childlist>
            <I>1</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
            <F>1</F>
        </Childlist>
        <Childlist>
            <I>11</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
            <F>11</F>
        </Childlist>
    </Tag>
</Root>

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="Tag">
        <Tag>
            <Childlist>
                <xsl:apply-templates select="node()[1]"/>
            </Childlist>
        </Tag>
    </xsl:template>
    <xsl:template match="*/Dlist[last()]" name="makeItem">
        <Item>
            <xsl:apply-templates select="node()[1]"/>
        </Item>
    </xsl:template>
    <xsl:template match="*/Dlist[1]">
        <xsl:copy>
            <xsl:call-template name="wrap"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()
                                         [not(self::Dlist)][1]"/>
    </xsl:template>
    <xsl:template match="Childlist|Dlist" name="wrap">
        <xsl:call-template name="makeItem"/>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
</xsl:stylesheet>

Output:

<Root>
    <Tag>
        <Childlist>
            <Item>
                <I>1</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
                <F>1</F>
            </Item>
            <Item>
                <I>11</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
                <F>11</F>
            </Item>
        </Childlist>
    </Tag>
</Root>

Note: This assumes Dlist elements are all next siblings. So Dlist[1] opens the new level and after that apply templates to next no Dlist node, and Dlist[last()] close the level not applying templates to next sibling.

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

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:key name="kDlist" match="Dlist"
 use="generate-id((.|preceding-sibling::Dlist)[1])"/>

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

 <xsl:template match="*[Childlist]">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <Childlist>
    <xsl:apply-templates/>
   </Childlist>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="Childlist|Dlist" name="makeItem">
  <Item>
   <xsl:apply-templates/>
  </Item>
 </xsl:template>

 <xsl:template match="Dlist[not(preceding-sibling::Dlist)]">
  <Dlist>
   <xsl:apply-templates select="key('kDlist', generate-id())" mode="copy"/>
  </Dlist>
 </xsl:template>

 <xsl:template match="Dlist" mode="copy">
  <xsl:call-template name="makeItem"/>
 </xsl:template>

 <xsl:template match="Dlist"/>
</xsl:stylesheet>

when applied on this XML document (the original text, corrected to be well-formed):

<Root>
    <Tag>
        <Childlist>
            <I>1</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
        </Childlist>
        <Childlist>
            <I>11</I>
            <Dlist>2</Dlist>
            <Dlist>3</Dlist>
            <Dlist>4</Dlist>
        </Childlist>
    </Tag>
</Root>

produces the wanted, correct result:

<Root>
    <Tag>
        <Childlist>
            <Item>
                <I>1</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
            </Item>
            <Item>
                <I>11</I>
                <Dlist>
                    <Item>2</Item>
                    <Item>3</Item>
                    <Item>4</Item>
                </Dlist>
            </Item>
        </Childlist>
    </Tag>
</Root>

Upvotes: 0

Related Questions