Move certain nodes that are sibling of select node into a new parent node
I did some searching here and found some questions that were related to my problem, but I'm still having trouble...(hope it's ok that I'm adding a new question instead of commenting on an existing one..)
<?xml version="1.0"?>
<section>
<title>Main Section</title>
<para>Here is some text that is a child of a main section.</para>
<para>Some more text.</para>
<para>When a section has subsections, it should not have loose paragraphs before the first sub section. Those loose paras should be placed inside a comment element.</para>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
I want to have the /section/para placed inside a newly created comment node, like so:
<?xml version="1.0"?>
<section>
<title>Main Section</title>
<comment>
<para>Here is some text that is a child of a main section.</para>
<para>Some more text.</para>
<para>When a section has subsections, it should not have loose paragraphs before the first sub section. Those loose paras should be placed inside a comment element.</para>
</comment>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
I tried some of the suggestions I found searching stackoverflow, the closest one is here.
This is the stylesheet I'm using:
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
开发者_StackOverflow中文版<xsl:output method="xml"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- paras that are children of a section that has direct para children and dircect section children -->
<xsl:template match="section[para][section]/para[1]">
<comment>
<xsl:apply-templates select="../para" mode="commentPara"/>
</comment>
</xsl:template>
<xsl:template match="*" mode="commentPara">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="section[para][section]/para"/>
</xsl:stylesheet>
It's outputting this:
<?xml version='1.0' ?>
<section>
<title>Main Section</title>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
simply deleting the paras I want to wrap in a comment tag. I tried going essentially line by line through the stylesheet in the question I linked to...any ideas? thanks, bp
That's grouping adjacents para
elements.
The problem is in Conflict Resolution for Template Rules: because both para
matching rules have the same import precedence and the same calculated priority, error recovery mechanism could lead to apply last one.
You need to explicitly define a @priority
on the "first para
child" rule like:
<xsl:template match="section[para][section]/para[1]" priority="1">
</
With such modification, the output is:
<?xml version="1.0" encoding="UTF-16"?>
<section>
<title>Main Section</title>
<comment>
<para>Here is some text that is a child of a main section.</para>
<para>Some more text.</para>
<para>When a section has subsections, it should not have loose paragraphs before the first sub section. Those loose paras should be placed inside a comment element.</para>
</comment>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
I would do it this way:
<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="kPreceding" match="para"
use="generate-id(following-sibling::section[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="section[preceding-sibling::para]">
<comment>
<xsl:apply-templates mode="copy"
select="key('kPreceding', generate-id())"/>
</comment>
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match=
"para[following-sibling::section]"/>
<xsl:template match="para" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<section>
<title>Main Section</title>
<para>Here is some text that is a child of a main section.</para>
<para>Some more text.</para>
<para>When a section has subsections, it should not have loose paragraphs before the first sub section. Those loose paras should be placed inside a comment element.</para>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
the wanted, correct result is produced:
<section>
<title>Main Section</title>
<comment>
<para>Here is some text that is a child of a main section.</para>
<para>Some more text.</para>
<para>When a section has subsections, it should not have loose paragraphs before the first sub section. Those loose paras should be placed inside a comment element.</para>
</comment>
<section>
<title>This is my subsection</title>
<para>Text that is inside of the sub-section</para>
<para>And some more sub section text.</para>
</section>
</section>
Explanation:
The identity rule (template) copies every node "as-is".
The overriding template for
section
that has preceding sibling(s)para
wraps all such siblings with acomment
element, then calls the identity transformation on itself.For convenience we define a key that matches all
para
elements preceding asection
element with a givengenerate-id()
.The
para
elements that have a following siblingsection
are excluded from the action of the identity rule by an overriding template, that simply does nothing.Finally, such
para
elements, when being output within the wrappercomment
are processed in modecopy
, which simply calls the identity rule to do the copying.
精彩评论