基于on属性包装兄弟节点

时间:2010-10-04 11:30:48

标签: xml xslt

使用XSLT,如何为属性包含共享相同值的兄弟节点。

让我们说我需要将一个或多个<amendment/>包含在它们所属的<chapter/>中。 由此:

<section>
      <heading>some heading text</heading>
      <amendment num='1' chapter='1'>
            <foo/>
      </amendment>
      <amendment num='2' chapter='1'>
            <bar/>
      </amendment>
      <amendment num='3' chapter='2'>
            <baz/>
      </amendment>
      <heading>some heading text</heading>
      <amendment num='4' chapter='3'>
            <baz/>
      </amendment>
</section>

进入这个:

<section>
      <heading>some heading text</heading>
      <chapter num="1">
            <amendment num='1'>
                  <foo/>
            </amendment>
            <amendment num='2'>
                  <bar/>
            </amendment>
      </chapter>
      <chapter num="2">
            <amendment num='3'>
                  <baz/>
            </amendment>
      </chapter>
      <heading>some heading text</heading>
      <chapter num="3">
            <amendment num='4'>
                  <baz/>
            </amendment>
      </chapter>
</section>

注1:修订始终按源XML中的章节列出。

注2:我使用PHP5和XSLT 1.0

4 个答案:

答案 0 :(得分:1)

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kAmendementByChapter" match="amendment" use="@chapter"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="amendment[count(.|key('kAmendementByChapter',
                                               @chapter)[1])=1]">
        <chapter num="{@chapter}">
            <xsl:apply-templates select="key('kAmendementByChapter',@chapter)"
                                 mode="copy"/>
        </chapter>
    </xsl:template>
    <xsl:template match="amendment"/>
    <xsl:template match="amendment" mode="copy">
        <xsl:call-template name="identity"/>
    </xsl:template>
    <xsl:template match="@chapter"/>
</xsl:stylesheet>

输出:

<section>
    <heading>some heading text</heading>
    <chapter num="1">
        <amendment num="1">
            <foo></foo>
        </amendment>
        <amendment num="2">
            <bar></bar>
        </amendment>
    </chapter>
    <chapter num="2">
        <amendment num="3">
            <baz></baz>
        </amendment>
    </chapter>
    <heading>some heading text</heading>
    <chapter num="3">
        <amendment num="4">
            <baz></baz>
        </amendment>
    </chapter>
</section>

注意:复制所有(缩进规则),在@chapter上进行分组。

答案 1 :(得分:0)

如果你正在使用XSLT 1,你可以使用这样的Muenchian分组方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="chapter" use="@chapter" match="amendment" />

  <xsl:template match="section">
    <xsl:copy>
      <xsl:apply-templates select="heading | amendment[generate-id() = generate-id(key('chapter',@chapter)[1])]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="amendment">
    <xsl:element name="chapter">
      <xsl:attribute name="num">
        <xsl:value-of select="@chapter" />
      </xsl:attribute>
      <xsl:apply-templates select="key('chapter', @chapter)" mode="withoutchapter"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="amendment" mode="withoutchapter">
    <xsl:copy>
      <xsl:apply-templates  select="@*[(name() != 'chapter')] | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

这里有两个'修正'模板 - 第一个(没有模式)仅由修改后的章节模板调用,这是第一次修改的章节。它创建了一个章节元素,并在其中调用每个amendment标签上的第二个模板。

这里有两个警告;首先,任何没有章节的修改都将从输出中删除。

其次,如果两个修正标签之间有标题,则修订标签仍会被分组,标题将出现在该组之后。

所以,如果你这样做(缩写为清晰):

<amendment num='1' chapter='1' />
<heading>heading text</heading>
<amendment num='2' chapter='1' />

它会输出:

<chapter num='1'>
  <amendment num='1' />
  <amendment num='2' />
</chapter>
<heading>heading text</heading>

答案 2 :(得分:0)

感谢@Alejandro和@Flynn1179注意到此解决方案初始版本中的错误 - 现已更正!

此转化

<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="kbyChapter" match="amendment"
     use="@chapter"/>

 <xsl:template match="node()" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="node()[1]"/>
     </xsl:copy>
     <xsl:apply-templates select="following-sibling::node()[1]"/>
 </xsl:template>

 <xsl:template match="amendment"/>
 <xsl:template match=
 "amendment[not(@chapter=preceding-sibling::amendment[1]/@chapter)]">
  <chapter num="{@chapter}">
   <xsl:apply-templates select="key('kbyChapter',@chapter)" mode="copy"/>
  </chapter>
  <xsl:apply-templates select=
    "key('kbyChapter',@chapter)[last()]/following-sibling::node()[1]"/>
 </xsl:template>

 <xsl:template match="*" mode="copy">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()[1]"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:copy-of select="."/>
 </xsl:template>

 <xsl:template match="@chapter"/>
</xsl:stylesheet>

应用于提供的XML文档时(对最后amendment进行了更正):

<section>
      <heading>some heading text</heading>
      <amendment num='1' chapter='1'>
            <foo/>
      </amendment>
      <amendment num='2' chapter='1'>
            <bar/>
      </amendment>
      <amendment num='3' chapter='2'>
            <baz/>
      </amendment>
      <heading>some heading text</heading>
      <amendment num='4' chapter='3'>
            <baz/>
      </amendment>
</section>

生成想要的正确结果

<section>
    <heading>some heading text</heading>
    <chapter num="1">
        <amendment num="1">
            <foo/>
        </amendment>
        <amendment num="2">
            <bar/>
        </amendment>
    </chapter>
    <chapter num="2">
        <amendment num="3">
            <baz/>
        </amendment>
    </chapter>
    <heading>some heading text</heading>
    <chapter num="3">
        <amendment num="4">
            <baz/>
        </amendment>
    </chapter>
</section>

答案 3 :(得分:0)

因为您的修改按章节排序,所以您可以不使用密钥,而只需查看紧随其后的元素。以下应该有效:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="amendment[not(@chapter = preceding-sibling::amendment[1]/@chapter)]">
  <chapter num="{@chapter}">
    <xsl:variable name="chapter" select="@chapter"/>
      <amendment num="{@num}">
        <xsl:apply-templates/>
      </amendment>
    <xsl:apply-templates select="following-sibling::amendment[1][@chapter = $chapter]">
      <xsl:with-param name="chapter" select="@chapter"/>
    </xsl:apply-templates>
  </chapter>
</xsl:template>

<xsl:template match="amendment">
  <xsl:param name="chapter"/>
  <xsl:if test="$chapter">
      <amendment num="{@num}">
        <xsl:apply-templates/>
      </amendment>
      <xsl:apply-templates select="following-sibling::amendment[1][@chapter = $chapter]">
        <xsl:with-param name="chapter" select="$chapter"/>
      </xsl:apply-templates>
  </xsl:if>
</xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>