How to map PCDATA between two empty tags into one tag?
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?
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<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.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<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>
精彩评论