Reputation: 21
How to change the tag placement using XSLT, but without mentioning the element name. i.e. we don't know what ar the element should be parent of <br/>
tag.
Example: If the <br/>
tag as a 1st child node of any element but not <p>
or <td>
. This case <br/>
tag should place outside of the element. Here I have given the input and required output:
Input:
<p>Test <b><i><br/>case</i></b></p>
Required Output:
<p>Test <br/><b><i>case</i></b></p>
Upvotes: 2
Views: 83
Reputation: 1458
I understand that you need to move <br>
as first child without preceding text up the tree to the <p>
or <td>
level. I have extended the examples to cover some more possible cases:
<test>
<!-- these cases are ok -->
<p>Test <br/>case</p>
<p>Test <br/><b>case</b></p>
<p>Test <br/><i>case</i></p>
<p>Test <i>case<br/></i></p>
<p>Test <b><i>case</i><br/></b></p>
<p>Test <b><i>case<br/>case<br/></i></b></p>
<!-- here should happen something -->
<p>Test <b><br/>case</b></p>
<p>Test <b><i><br/>case</i></b></p>
<p>Test <i><br/><b>case</b></i></p>
<!-- mixed case -->
<p>Test <i><b><br/>case<br/></b></i></p>
<!-- edit: additional example -->
<p><br/><x>0.03-</x></p>
</test>
Now, the replacement procedure needs to components: First, removing all br
which are in the wrong place, and secondly, create a copy of them in the desired position. Identifying the relevant br
works this way: If a preceding-sibling or their descendant is a text node, the br
needs to remain where it is. In other cases, it can be copied up the tree. That's it. All other elements can be copied through an identity template.
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="p/*
[descendant::br
[not(preceding-sibling::node()
/descendant-or-self::text())]]">
<xsl:copy-of select="descendant::br
[not(preceding-sibling::node()
/descendant-or-self::text())]"/>
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!--<xsl:template match="p//br
[not(preceding-sibling::node()/descendant-or-self::text())]"/>-->
<!-- edit: fixed version -->
<xsl:template match="p/*//br
[not(preceding-sibling::node()
/descendant-or-self::text())]"/>
This should produce the following result:
<test>
<!-- these cases are ok -->
<p>Test <br/>case</p>
<p>Test <br/><b>case</b></p>
<p>Test <br/><i>case</i></p>
<p>Test <i>case<br/></i></p>
<p>Test <b><i>case</i><br/></b></p>
<p>Test <b><i>case<br/>case<br/></i></b></p>
<!-- here should happen something -->
<p>Test <br/><b>case</b></p>
<p>Test <br/><b><i>case</i></b></p>
<p>Test <br/><i><b>case</b></i></p>
<!-- mixed case -->
<p>Test <br/><i><b>case<br/></b></i></p>
<!-- edit: additional example -->
<p><br/><x>0.03-</x></p>
</test>
Upvotes: 0
Reputation: 25034
Start with the identity transform. Give a name (e.g. idt-elem
) to the template for elements.
Add a template that matches any element whose first child is a br
. If your input is XHTML, and you've bound the prefix xh
to the XHTML namespace, a possible match pattern for this template is *[child::node()[1]/self::xh:br]
. In that template, first emit a br
element and then call template idt-elem
.
If this is supposed to match only elements other than p
and td
, then add a higher-priority for those two elements, which calls idt-elem
.
Add a template for br
elements which are the first child of any element other than p
and td
. Make it do nothing.
The resulting stylesheet will move initial br
elements out of their parents and make them immediate left siblings of their former parent. To handle cases like your <b><i><br/>...</i></b>
you will need to run the stylesheet twice. In general, run it until its output no longer differs from its input. (Or add a template for the root node that tests whether any br
elements are going to be moved and issues a message otherwise.)
Upvotes: 1