Reputation: 19385
Given the following XML document:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
</dialog>
I'd like to try and capture eveything within speech
that isn't in a line
already and wrap it in a line
, however I need to capture any other elements along with it (eg. the emphasis
in the example above).
The result I'd like to achieve is:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
<line>I can't believe him. I just <emphasis>can't</emphasis> believe him!</line>
</speech>
</dialog>
I'm using libxslt
and libxml
, so I'm stuck with XSLT 1.0.
Upvotes: 0
Views: 149
Reputation: 167571
One way to approach that in XSLT 1.0 is through sibling recursion, as outlined in the following example:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="@* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech/line">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and (not(preceding-sibling::node())
or
preceding-sibling::node()[1][self::line])]">
<xsl:if test="normalize-space() or following-sibling::node()[1][not(self::line)]">
<line>
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</line>
</xsl:if>
<xsl:apply-templates select="following-sibling::line[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and preceding-sibling::node()[1][not(self::line)]]">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</xsl:template>
</xsl:stylesheet>
With that stylesheet an input sample like
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
<speech speaker="Foo">This is a test.
<line>This line is wrapped and should be copied unchanged.</line>
<em>This</em> needs to be <it>wrapped</it>.
</speech>
<speech speaker="Bar"> <em>This</em> should be wrapped.
<line>This line is wrapped and should be copied unchanged.</line>
<it>Test</it>
</speech>
</dialog>
is transformed into
<dialog>
<speech speaker="Robert"><line>"Once more into the breach", he said.</line><line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</line></speech>
<speech speaker="Foo"><line>This is a test.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<em>This</em> needs to be <it>wrapped</it>.
</line></speech>
<speech speaker="Bar"><line> <em>This</em> should be wrapped.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<it>Test</it>
</line></speech>
</dialog>
Upvotes: 2