Reputation: 33
I've got an XML file which is something like :
<doc>
<element>
<word>
<text>one word</text>
</word>
<word>
<text>two words</text>
</word>
</element>
<element>
<other>
<text>the sky</text>
</other>
<word>
<text>NN NN</text>
</word>
</element>
</doc>
And I'd like to have just one tag and the content of the lines when there are two on after the other, like that :
<element>
<word>
<text>one word</text>
<text>two words</text>
</word>
</element>
The problem is that I'm generating an <xsl:element>
in order to get the <word>
tags. And this <xsl:element>
is already in a <xsl:for-each>
loop that puts the <word>
tags in the right order (filtering on an attribute).
My previous XML doc is like that :
<doc>
<element class="word">
<text>one word</text>
</element>
<element class="word">
<text>NN NN</text>
</element>
<element class="word">
<text>two words</text>
</element>
<element class="other">
<text>the sky</text>
</element>
</doc>
Any help would be great, thank you :)
-- More details --
This is the result I want to have :
<doc>
<element>
<word>
<text>one word</text>
<text>two words</text>
</word>
</element>
<element>
<other>
<text>the sky</text>
</other>
<word>
<text>NN NN</text>
</word>
</element>
</doc>
And I can't specify the tags word or other because I don't have a closed list of tags. So I'm using xsl:variable :
<xsl:for-each select="element">
<xsl:variable name="name">
<xsl:value-of select="@class"/>
</xsl:variable>
<xsl:element name="{$name}">
<text>
<xsl:value-of select="."/>
</text>
</xsl:element>
</xsl:for-each>
Upvotes: 0
Views: 1158
Reputation: 12075
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="element/*">
<xsl:if test="not(preceding-sibling::*[name() = name(current())])">
<xsl:copy>
<xsl:apply-templates select="* | following-sibling::*[name()=name(current())]/*" />
</xsl:copy>
</xsl:if>
</xsl:template>
<!-- Identity template -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It simply looks for any child nodes of element
that do not have a previous sibling with the same name (i.e., the first of each one), and then applies templates to it's own child nodes and the child nodes of any following siblings with the same name.
Upvotes: 1
Reputation: 243459
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:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="element/*"/>
<xsl:template match="element/*[1]">
<word>
<xsl:apply-templates select="../*/*"/>
</word>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<doc>
<element>
<word>
<text>one word</text>
</word>
<word>
<text>two words</text>
</word>
</element>
<element>
<other>
<text>the sky</text>
</other>
<word>
<text>NN NN</text>
</word>
</element>
</doc>
produces (what I guess is) the wanted, correct result:
<doc>
<element>
<word>
<text>one word</text>
<text>two words</text>
</word>
</element>
<element>
<word>
<text>the sky</text>
<text>NN NN</text>
</word>
</element>
</doc>
Upvotes: 0
Reputation: 52848
You can "unwrap" an element by just using <xsl:apply-templates/>
...
XML Input
<doc>
<element>
<word>
<text>one word</text>
</word>
<word>
<text>two words</text>
</word>
</element>
<element>
<other>
<text>the sky</text>
</other>
<word>
<text>NN NN</text>
</word>
</element>
</doc>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="element">
<xsl:copy>
<word>
<xsl:apply-templates/>
</word>
</xsl:copy>
</xsl:template>
<xsl:template match="word|other">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output
<doc>
<element>
<word>
<text>one word</text>
<text>two words</text>
</word>
</element>
<element>
<word>
<text>the sky</text>
<text>NN NN</text>
</word>
</element>
</doc>
Upvotes: 0