使用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
答案 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>