tags:

views:

55

answers:

4

Hi there,

I need to find out if anything exists between two nodes. My XML looks like this:

<event value1="1" value2="2" value3="3" info="some info here">
          Line 1.<lb/>
          Line 2.<lb/><lb/>
          Line 3.<lb/>
          Line 4.<lb/>
</event>

My goal is to convert the <lb/> nodes to <br/> HTML tags using XSLT. There is one additional requirement to fulfill though. In case there is one <lb/> directly following another <lb/> I want to output only one <br/>.

My XSLT looks like this:

 <xsl:template match="lb">
    <xsl:if test="not(preceding-sibling::lb[1])">
       <br/>
    </xsl:if>
 </xsl:template>

The problem with the XSLT above is that it works correctly for line 1 only as the text between both nodes is ignored.

Maybe someone here can help.

+2  A: 

Edit: Another, more efficient way is this:

<xsl:template match="lb">
    <xsl:if test="following-sibling::node()[1][not(self::lb)]">
        <br/>
    </xsl:if>
</xsl:template>

Try this:

 <xsl:template match="lb">
   <xsl:if test="generate-id()=generate-id(preceding-sibling::text()[1]/following-sibling::lb[1])">
     <br/>
   </xsl:if>
 </xsl:template>

Basically this checks if you are on the first <lb/> the previous text sibling node, which also means that it will only add <br/> after some text, but not if the <lb/> are at the beginning of the parent tag.

Lucero
This does not seem to work. As far as I know it would but only if text was in its own note.
sebastian
I tried with the .NET XSLT engine and it does work fine.
Lucero
This is a good start but it will fail for more than two consecutive `<lb>`s.
Tomalak
@Tomalak, how so? I just tried both my solutions and they both work as expected.
Lucero
+2  A: 

You are looking for something along the lines of this:

<!-- per default, <lb> gets removed (empty template)... -->
<xsl:template match="lb" />

<!-- ...unless it is directly preceded by a non-blank text node -->
<xsl:template match="lb[
  normalize-space(preceding-sibling::node()[1][self::text()]) != ''
]">
  <br />
</xsl:template>

This handles any arbitrary number of <lb/> nodes that may occur consecutively.

Behavior for <lb/> nodes that follow an element (not a text node) must still be defined. Currently they would be deleted. If you want to keep them, write an appropriate template for them.

Tomalak
I wonder why it hasn't been accepted !? +1 for compact solution.
infant programmer
+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="lb"/>

 <xsl:template match=
  "lb[not(following-sibling::node()[1][self::lb])]">
  <br />
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<event value1="1" value2="2" value3="3" info="some info here">
          Line 1.<lb/>
          Line 2.<lb/><lb/>
          Line 3.<lb/>
          Line 4.<lb/>
</event>

produces the wanted result:

<event value1="1" value2="2" value3="3" info="some info here">
          Line 1.<br/>
          Line 2.<br/>
          Line 3.<br/>
          Line 4.<br/>
</event>

Do note:

  1. The use and overriding of the identity transform.

  2. How <lb> elements are deleted by default.

  3. How only the last <lb> element in a group of adjacent sibling <lb> nodes is processed in a more special way -- is replaced by a <br /> element.

Dimitre Novatchev
A: 

Thanks a lot to everyone.

I am using Lucero's 2nd solution right now and it seems to work fine. Nevertheless I will do some experiments with the other solutions provided for learning purposes.

Thanks again to all of you for helping me this quickly. Sebastian

sebastian