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

时间:2015-09-22 17:23:16

标签: xml xslt xslt-2.0

我有一个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>

正如您所看到的,它为<ss_start/><ss_end/>分别添加了<p id="st_main"><p id="st_chap">。但我需要从<p>开始识别具有at id的连续st元素,并按<ss_start/><ss_end/>覆盖这些节点。

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

2 个答案:

答案 0 :(得分:2)

如果您只是使用

<?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>

答案 1 :(得分:2)

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

<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>