tags:

views:

72

answers:

2

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]

+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <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>
Dimitre Novatchev
@Dimitre: +1 Good answer! This remains me http://stackoverflow.com/questions/3652194/wrap-group-of-xml-nodes/3653382 . So I'm adding one, too.
Alejandro
@Dimitre: A minor: I think you should strip `Dlist` from pattern `Childlist|Dlist` just for clarity, anyway error recovery mechanism will select last empty template matching `Dlist`
Alejandro
@Dimitre: Thanks for your reply. :)
Syam
+1  A: 

Besides Dimitre's excellent answer, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <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"&gt;
    <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.

Alejandro
@Alejandro: You know I have a crush on fine-grained identity? :) +1.
Dimitre Novatchev
@Dimitre: I also like it very much, ja! I should check if this pattern follows the restrictions for streaming in XSLT 2.1... I think it doesn't.
Alejandro
@Alejandro: Could you please give and xsl to do the reverse conversion? Also could you suggest some good tutorials for xslt? :)
Syam
@Syam: Reverse transformation is very straightforward. Use identity transform, strip `Childlist|Dlist` with empty template, transform `Childlist/Item` to `Childlist` and `Dlist/Item` to `Dlist`. First thing for learning XSLT is to grasp declarative paradigm (maybe Haskell, Miranda or some general paper) then to study XSLT and XPath specs, and last check FAQs http://www.dpawson.co.uk/xsl/ XSLT List http://www.mulberrytech.com/xsl/xsl-list/ SO Top Answers blogs, etc.
Alejandro
@Alejandro: Thanks a Million.. :)
Syam
@Syam: You're wellcome! Ask any time.
Alejandro
@Alejandro: I have a problem with this xls. If i add any tag below Dlist it gets appended to Dlist.. Any idea!! Sorry to disturn you again.. :(
Syam
@Syam: No problem, but good practice is to ask another question. You need to clarify if `Dlist` elements are going to be all siblings and there are going to be other preceding and following elements or there are goint to be other elements between `Dlist` elements. For both schemas, modifications are going to be minor but diferent.
Alejandro
@Alejandro: There are going to be other preceding and following elements
Syam
@Syam: Check my update.
Alejandro