tags:

views:

27

answers:

2

Hi All, I have this following doubt in xslt coding.

my input is : <text><p>some text</p> <p/>some text <emph>....</emph>.........<p/> </text>

And the output that i need is

<text><p>some text</p><p>some text <emph>....</emph>.........</p></text>

How am i supposed to map the values present between two empty "p" tags in to a non-empty "p" tag?

+1  A: 

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="p[not(node())][last()][count(../p[not(node())]) mod 2]" priority="1">
        <xsl:call-template name="identity"/>
    </xsl:template>
    <xsl:template match="p[not(node())][not(position() mod 2)]" priority="1"/>
    <xsl:template match="p[not(node())]">
            <p>
                <xsl:apply-templates select="following-sibling::node()[1]"/>
            </p>
            <xsl:apply-templates select="following-sibling::p[not(node())][1]/following-sibling::node()[1]"/>
    </xsl:template>
</xsl:stylesheet>

With inputs:

1 - (p with content and two empty p)

<text><p>some text</p> <p/>some text <emph>....</emph>.........<p/> </text>

2 - (Four empty p)

<text><p/>some text<p/> <p/>some text <emph>....</emph>.........<p/> </text>

3 - (Three empty p)

<text><p/>some text <p/>some text <emph>....</emph>.........<p/> </text>

4 - (p with content and three empty p)

<text><p/>some text <p/>some text <p><emph>....</emph></p>.........<p/> </text>

5 - (p with content two empty p siblings and other level two empty p)

<text><p>some text</p> <p/>some text <emph><p/>....<p/></emph>.........<p/> </text>

Results:

1 -

<text><p>some text</p><p>some text <emph>....</emph>.........</p></text>

2 -

<text><p>some text</p><p>some text <emph>....</emph>.........</p></text>

3 -

<text><p>some text </p>some text <emph>....</emph>.........<p></p></text>

4 -

<text><p>some text </p>some text <p><emph>....</emph></p>.........<p></p></text>

5 -

<text><p>some text</p><p>some text <emph><p>....</p></emph>.........</p></text>

Note: Breaking the recursion and following node by node in "serial" way.

EDIT: I think that now it covers every case. Take notice that you can't define with your format when you had odds p wich of the two (preceding or following) want to enclosed. So, this is "associative" left to rigth.

EDIT 2: Better use of last() (How did I miss that?)

EDIT 3: Better pattern matching allow to compact code.

Alejandro
+1 for very good use of the most fine-grained tree traversal, for extensive testing and for pointing out an issue in my own solution that I have now fixed. Good job, Alejandro.
Dimitre Novatchev
+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:strip-space elements="*"/>

 <xsl:key name="kfollowingNodes"
 match="node()[not(self::p[not(node())])]"
 use="generate-id(preceding-sibling::p[not(node())][1])"/>

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

 <xsl:template match=
 "p[not(node()) and following-sibling::p[not(node())]]">
  <xsl:variable name="vFollowing"
       select="key('kfollowingNodes', generate-id())"/>
  <xsl:if test="$vFollowing">
      <p>
       <xsl:apply-templates mode="wrap" select=
       "key('kfollowingNodes', generate-id())"/>
      </p>
  </xsl:if>
 </xsl:template>

 <xsl:template match=
 "p[not(node()) and not(following-sibling::p[not(node())])]"/>

 <xsl:template match="node()[not(self::p[not(node())])][preceding-sibling::p[not(node())]]"/>

 <xsl:template match="node()" mode="wrap">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<text>
    <p>some text</p>
    <p/>some other text
    <emph>Hello World</emph>
    yet another text
    <p/>
</text>

produces the desired output:

<text>
    <p>some text</p>
    <p>some other text
    <emph>Hello World</emph>
    yet another text
    </p>
</text>
Dimitre Novatchev
@Dimitre: When running your transformation with my second case, result: `<text><p>some text</p></text>`. This was a challenge to me, but your transformation really help me with border case.
Alejandro
@Alejandro: Thank you, I have fixed this.
Dimitre Novatchev
@Dimitre: I was a litlle "dry" last time... First, +1 for key and complex pattern matching (I was fighting a good time with it!). Second, I think it should be detail that with this solution both sides (left and right) of odds empty `p` (my 3rd example) gets enclosed, wich is an also fine decision given that the format can't define behavior by itself.
Alejandro