2010-10-04 165 views
2

使用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:IM使用PHP5用XSLT 1.0

+1

最后一个'revision'标签上的输入XML是否缺少'chapter num =“3”'? – Flynn1179 2010-10-04 12:52:03

+1

你打算使用XSLT 1还是2?更容易2 – 2010-10-04 12:56:05

+0

我已更新问题以回答这些问题。 – 2010-10-04 13:10:27

回答

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> 

注意:复制所有(的indentity规则),对@chapter分组。

+0

@Alejandro:+1为最佳答案! – 2010-10-04 16:56:56

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

这些章节元素应该像这样嵌套吗? – Flynn1179 2010-10-04 13:27:32

+0

我认为Flynn1179是对的。对于细粒度遍历,您需要关闭规则,如' 2010-10-04 13:46:42

+0

@Ajjandro,@ Flynn1179:感谢您注意这一点。现在修复。当我解决这个问题的时候,我几乎已经迟到了,而且显示出来了:) – 2010-10-04 16:54:15

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>