Prabhu M
Prabhu M

Reputation: 21

How to change the tag placement using XSLT, but without mentioning the node name

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

Answers (2)

friedemann_bach
friedemann_bach

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

C. M. Sperberg-McQueen
C. M. Sperberg-McQueen

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

Related Questions