Reputation: 13
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
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
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