XSLT - 通过分析属性值进行分组

XSLT - Grouping by analyzing attribute values

我有一个 xml 如下,

<doc>
    <section>
        <p id="main">aa</p>
        <p id="main">bb</p>
        <p id="main">cc</p>
        <p id="para1">dd</p>
        <p id="st_main">ee</p>
        <p id="st_chap">ff</p>
        <p id="st_chap">gg</p>
        <p id="st_main">hh</p>
        <p id="main">ii</p>
        <p id="main">cc</p>
        <p id="para2">xx</p>
        <p id="main">yy</p>
        <p id="main">cc</p>
    </section>
</doc>

我的要求是

1) 按 para 属性分组 <p> 并向每个 <p> 组添加单独的部分。

2) 识别 <p> 节点组,其 id 属性从 st 开始,将 <st_start><st_end> 放在组的开始和结束处

所以我想要的输出是,

<doc>
    <section>
        <p id="main">aa</p>
        <p id="main">bb</p>
        <p id="main">cc</p>
    </section>
    <section type="para1">
        <p id="para1">dd</p>
        <ss_start/>
        <p id="st_main">ee</p>
        <p id="st_chap">ff</p>
        <p id="st_chap">gg</p>
        <p id="st_main">hh</p>
        <ss_end/>
        <p id="main">ii</p>
        <p id="main">cc</p>
    </section>
    <section type="para2">
        <p id="para2">xx</p>
        <p id="main">yy</p>
        <p id="main">cc</p>
    </section>
</doc>

我实现这个任务的xsl如下,

<xsl:template match="section">
        <xsl:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
            <section>
                <xsl:if test="current-group()[1][not(@id='main')]">
                    <xsl:attribute name="type" select="current-group()[1]/@id"/>
                </xsl:if>
                <xsl:for-each-group select="current-group()" group-adjacent="@id">
                    <xsl:if test="starts-with(current-grouping-key(),'st')">
                        <ss_start/>
                    </xsl:if>            
                    <xsl:apply-templates select="current-group()"/>
                    <xsl:if test="starts-with(current-grouping-key(),'st')">
                        <ss_end/>
                    </xsl:if>
                </xsl:for-each-group>
            </section>
        </xsl:for-each-group>
    </xsl:template>

这个 xsl 给了我以下结果,

<doc>
    <section>
        <p id="main">aa</p>
        <p id="main">bb</p>
        <p id="main">cc</p>
    </section>
    <section type="para1">
        <p id="para1">dd</p>
        <ss_start/>
        <p id="st_main">ee</p>
        <ss_end/>
        <ss_start/>
        <p id="st_chap">ff</p>
        <p id="st_chap">gg</p>
        <ss_end/>
        <ss_start/>
        <p id="st_main">hh</p>
        <ss_end/>
        <p id="main">ii</p>
        <p id="main">cc</p>
    </section>
    <section type="para2">
        <p id="para2">xx</p>
        <p id="main">yy</p>
        <p id="main">cc</p>
    </section>
</doc>

如您所见,它分别为 <p id="st_main"><p id="st_chap"> 添加了 <ss_start/><ss_end/>。但我需要从 st 开始识别具有 attr id 的连续 <p> 元素,并用 <ss_start/><ss_end/>.

覆盖这些节点

任何人都可以建议我如何修改我的代码以获得预期的结果吗?

如果你只是使用

<?xml version="1.0" encoding="UTF-8" ?>
<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:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
            <section>
                <xsl:if test="current-group()[1][not(@id='main')]">
                    <xsl:attribute name="type" select="current-group()[1]/@id"/>
                </xsl:if>
                <xsl:for-each-group select="current-group()" group-adjacent="starts-with(@id, 'st_')">
                    <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>
            </section>
        </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

然后你得到输出

<doc>
    <section>
      <p id="main">aa</p>
      <p id="main">bb</p>
      <p id="main">cc</p>
   </section>
   <section type="para1">
      <p id="para1">dd</p>
      <ss_start/>
      <p id="st_main">ee</p>
      <p id="st_chap">ff</p>
      <p id="st_chap">gg</p>
      <p id="st_main">hh</p>
      <ss_end/>
      <p id="main">ii</p>
      <p id="main">cc</p>
   </section>
   <section type="para2">
      <p id="para2">xx</p>
      <p id="main">yy</p>
      <p id="main">cc</p>
   </section>
</doc>

这可能是您最简单的解决方案...

<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="section">
  <xsl:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
    <section>
      <xsl:apply-templates select="current-group()[1]/@id" mode="section-type" />
      <xsl:apply-templates select="current-group()" />
    </section>
  </xsl:for-each-group>
</xsl:template>

<xsl:template match="@id[starts-with(., 'para')]" mode="section-type">
  <xsl:attribute name="type" select="." />
</xsl:template>

<xsl:template match="@*" mode="section-type" />

<xsl:template match="p[starts-with(@id, 'st')]
                      [not(starts-with(preceding-sibling::p[1]/@id, 'st'))]">
  <ss_start />
  <xsl:next-match />
</xsl:template>

<xsl:template match="p[starts-with(@id, 'st')]
                      [not(starts-with(following-sibling::p[1]/@id, 'st'))]">
  <xsl:next-match />
  <ss_end/>
</xsl:template>

</xsl:stylesheet>