XSLT - 识别具有相同属性值模式的连续节点
XSLT - Identify consecutive nodes which has same patterns of attribute values
我有xml这样的,
<section>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<p id="ss_main">ff</p>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
</section>
我的要求是通过覆盖现有节点 并以 ss
开头来放置名为 <ss_start>
和 <ss_end>
的新节点。
所以输出应该是,
<section>
<ss_start/>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<ss_end/>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<ss_start/>
<p id="ss_main">ff</p>
<ss_end/>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<ss_start/>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
<ss_end/>
</section>
我可以像下面这样编写 xsl 以覆盖特定节点 <ss_start>
和 <ss_end>
<xsl:template match="p[@id='ss_main']">
<ss_start/>
<p id="ss_main"><xsl:apply-templates/></p>
<ss_end/>
</xsl:template>
但我正在努力寻找 id attr 从 ss
开始的连续节点,并用 <ss_start>
和 <ss_end>
覆盖它们。
谁能给我一个方法,我该怎么做?
XSLT 1.0 兄弟递归
在 XSLT 1.0 中,您可以按如下方式执行此操作,它使用称为 sibling recursion 的技术(尽管 sibling traversal 可能是更好的方法学期)。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:apply-templates select="*[1]" />
</xsl:copy>
</xsl:template>
<xsl:template match="section/*[starts-with(@id, 'ss')]" priority="5">
<xsl:if test="self::*[not(preceding-sibling::*[1][starts-with(@id, 'ss')])]">
<ss_start />
</xsl:if>
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
<xsl:if test="self::*[not(following-sibling::*[1][starts-with(@id, 'ss')])]">
<ss_end />
</xsl:if>
<xsl:apply-templates select="following-sibling::*[1]" />
</xsl:template>
<xsl:template match="section/*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
<xsl:apply-templates select="following-sibling::*[1]" />
</xsl:template>
</xsl:stylesheet>
其中,当 运行 反对您的输入时,将创建此输出:
<?xml version="1.0" encoding="UTF-8"?>
<section>
<ss_start/>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<ss_end/>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<ss_start/>
<p id="ss_main">ff</p>
<ss_end/>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<ss_start/>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
<ss_end/>
</section>
我现在看到你用 xslt-2.0 标记了你的问题,这意味着你可以使用分组。我将尝试使用 XSLT 2.0 中的示例进行更新。
XSLT 2.0 组相邻
在 XSLT 2.0 中,您可以使用布尔值 true/false 作为 group-adjacent 分组键,如下所示,它比上面的 XSLT 1.0 代码短很多:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="starts-with(@id, 'ss')">
<xsl:if test="current-grouping-key()"><ss_start /></xsl:if>
<xsl:apply-templates select="current-group()" />
<xsl:if test="current-grouping-key()"><ss_end /></xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
您也可以使用...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes" encoding="utf-8" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="p[starts-with(@id, 'ss')]
[not(starts-with(preceding-sibling::p[1]/@id, 'ss'))]">
<ss_start />
<xsl:next-match />
</xsl:template>
<xsl:template match="p[starts-with(@id, 'ss')]
[not(starts-with(following-sibling::p[1]/@id, 'ss'))]">
<xsl:next-match />
<ss_end/>
</xsl:template>
</xsl:stylesheet>
我有xml这样的,
<section>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<p id="ss_main">ff</p>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
</section>
我的要求是通过覆盖现有节点 并以 ss
开头来放置名为 <ss_start>
和 <ss_end>
的新节点。
所以输出应该是,
<section>
<ss_start/>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<ss_end/>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<ss_start/>
<p id="ss_main">ff</p>
<ss_end/>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<ss_start/>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
<ss_end/>
</section>
我可以像下面这样编写 xsl 以覆盖特定节点 <ss_start>
和 <ss_end>
<xsl:template match="p[@id='ss_main']">
<ss_start/>
<p id="ss_main"><xsl:apply-templates/></p>
<ss_end/>
</xsl:template>
但我正在努力寻找 id attr 从 ss
开始的连续节点,并用 <ss_start>
和 <ss_end>
覆盖它们。
谁能给我一个方法,我该怎么做?
XSLT 1.0 兄弟递归
在 XSLT 1.0 中,您可以按如下方式执行此操作,它使用称为 sibling recursion 的技术(尽管 sibling traversal 可能是更好的方法学期)。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:apply-templates select="*[1]" />
</xsl:copy>
</xsl:template>
<xsl:template match="section/*[starts-with(@id, 'ss')]" priority="5">
<xsl:if test="self::*[not(preceding-sibling::*[1][starts-with(@id, 'ss')])]">
<ss_start />
</xsl:if>
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
<xsl:if test="self::*[not(following-sibling::*[1][starts-with(@id, 'ss')])]">
<ss_end />
</xsl:if>
<xsl:apply-templates select="following-sibling::*[1]" />
</xsl:template>
<xsl:template match="section/*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
<xsl:apply-templates select="following-sibling::*[1]" />
</xsl:template>
</xsl:stylesheet>
其中,当 运行 反对您的输入时,将创建此输出:
<?xml version="1.0" encoding="UTF-8"?>
<section>
<ss_start/>
<p id="ss_main">aa</p>
<p id="ss_chap">bb</p>
<ss_end/>
<p id="main">cc</p>
<p id="main">dd</p>
<p id="main">ee</p>
<ss_start/>
<p id="ss_main">ff</p>
<ss_end/>
<p id="main">gg</p>
<p id="main">hh</p>
<p id="main">ii</p>
<p id="main">jj</p>
<ss_start/>
<p id="ss_chap">xx</p>
<p id="ss_main">yy</p>
<p id="ss_chap">zz</p>
<ss_end/>
</section>
我现在看到你用 xslt-2.0 标记了你的问题,这意味着你可以使用分组。我将尝试使用 XSLT 2.0 中的示例进行更新。
XSLT 2.0 组相邻
在 XSLT 2.0 中,您可以使用布尔值 true/false 作为 group-adjacent 分组键,如下所示,它比上面的 XSLT 1.0 代码短很多:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="starts-with(@id, 'ss')">
<xsl:if test="current-grouping-key()"><ss_start /></xsl:if>
<xsl:apply-templates select="current-group()" />
<xsl:if test="current-grouping-key()"><ss_end /></xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
您也可以使用...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes" encoding="utf-8" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="p[starts-with(@id, 'ss')]
[not(starts-with(preceding-sibling::p[1]/@id, 'ss'))]">
<ss_start />
<xsl:next-match />
</xsl:template>
<xsl:template match="p[starts-with(@id, 'ss')]
[not(starts-with(following-sibling::p[1]/@id, 'ss'))]">
<xsl:next-match />
<ss_end/>
</xsl:template>
</xsl:stylesheet>