我经历了XSLT Grouping Examples和Using for-each-group for high performance XSLT。我对for-each-group有问题。
我的XML
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p name="h-title" other="main">Introduction</p>
<p name="h1-title " other="other-h1">XSLT and XQuery</p>
<p name="h2-title" other=" other-h2">XSLT</p>
<p name="">
<p1 name="bold"> XSLT is used to write stylesheets.</p1>
</p>
<p name="h2-title " name="other-h2">XQuery</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h3-title" name="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h1-title " other="other-h1">XSLT and XQuery</p>
<p name="h2-title " other=" other-h2">XSLT</p>
</body>
我想要的输出
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p name="h-title " other="main">Introduction</p>
<h1>
<p name="h1-title " other="other-h1"> XSLT and XQuery </p>
<h2>
<p name="h2-title " other="other-h2">XSLT</p>
<p name="">
<p1 name="bold">XSLT is used to write stylesheets.
</p1>
</p>
</h2>
<h2>
<p name="h2-title " other="other-h2"> XQuery is used to query XML databases
</p>
<p name="">
<p name="bold"> XQuery is used to query XML databases.</p>
</p>
<h3>
<p name="h3-title " name="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
</h3>
</h2>
</h1>
<h1>
<p name="h1-title " other="other-h1">XSLT and XQuery</p>
<h2>
<p name="h2"-title other=" other-h2">XSLT</p>
</h2>
</h1>
</body>
我试过这个。 (不工作)
<xsl:template match="body">
<body>
<xsl:for-each-group select="*" group-starting-with="@h1-title" >
<h1>
<xsl:for-each select="current-group()[self:: h1-title]">
<xsl:value-of select="."/>
</xsl:for-each>
</h1>
</xsl:for-each-group>
<xsl:for-each-group select="*" group-starting-with="@h2-title" >
<h2>
<xsl:for-each select="current-group()[self::h2-title/@h2-title]">
<xsl:value-of select="."/>
</xsl:for-each>
</h2>
</xsl:for-each-group>
<xsl:for-each-group select="*" group-starting-with="@h3-title" >
<h3>
<xsl:for-each select="current-group()[self::h2-title/@h3-title]">
<xsl:value-of select="."/>
</xsl:for-each>
</h3>
</xsl:for-each-group>
</body>
</xsl:template>
有人会告诉我正确的方法来获得我想要的结果吗?
答案 0 :(得分:5)
这是一个在递归函数中使用for-each-group
的XSLT 2.0样式表(我更喜欢使用XSLT 2.0的命名模板):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:param name="prefix" as="xs:string" select="'h'"/>
<xsl:param name="suffix" as="xs:string" select="'-title'"/>
<xsl:output method="html" version="4.0" indent="yes"/>
<xsl:function name="mf:group" as="node()*">
<xsl:param name="items" as="node()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$items" group-starting-with="p[@name = concat($prefix, $level, $suffix)]">
<xsl:choose>
<xsl:when test="not(self::p[@name = concat($prefix, $level, $suffix)])">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="h{$level}">
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:copy>
<xsl:sequence select="mf:group(*, 1)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
当我将带有Saxon 9的样式表应用于输入
时<body>
<p name="h-title" other="main">Introduction</p>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<p name="h2-title" other=" other-h2">XSLT</p>
<p name="">
<p1 name="bold"> XSLT is used to write stylesheets.</p1>
</p>
<p name="h2-title" other="other-h2">XQuery</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h3-title" other="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<p name="h2-title" other=" other-h2">XSLT</p>
</body>
我得到了结果
<body>
<p name="h-title" other="main">Introduction</p>
<h1>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<h2>
<p name="h2-title" other=" other-h2">XSLT</p>
<p name="">
<p1 name="bold"> XSLT is used to write stylesheets.</p1>
</p>
</h2>
<h2>
<p name="h2-title" other="other-h2">XQuery</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<h3>
<p name="h3-title" other="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
</h3>
</h2>
</h1>
<h1>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<h2>
<p name="h2-title" other=" other-h2">XSLT</p>
</h2>
</h1>
</body>
答案 1 :(得分:3)
此转换使用键和句柄h1-title
到h6-title
:
<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:template match="body">
<xsl:apply-templates select="p[@name='h1-title']" />
</xsl:template>
<xsl:key name="next-headings" match="p[@name='h6-title']"
use="generate-id(preceding-sibling::p
[ @name='h1-title'
or @name='h2-title'
or @name='h3-title'
or @name='h4-title'
or @name='h5-title'
][1])" />
<xsl:key name="next-headings" match="p[@name='h5-title']"
use="generate-id(preceding-sibling::p
[ @name='h1-title'
or @name='h2-title'
or @name='h3-title'
or @name='h4-title'
][1])" />
<xsl:key name="next-headings" match="p[@name='h4-title']"
use="generate-id(preceding-sibling::p
[ @name='h1-title'
or @name='h2-title'
or @name='h3-title'
][1])" />
<xsl:key name="next-headings" match="p[@name='h3-title']"
use="generate-id(preceding-sibling::p
[ @name='h1-title'
or @name='h2-title'
][1])" />
<xsl:key name="next-headings" match="p[@name='h2-title']"
use="generate-id(preceding-sibling::p
[@name='h1-title'][1])" />
<xsl:key name="immediate-nodes" match=
"node()[not(self::p)
or
not(contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
concat('|',@name,'|')
)
)]"
use="generate-id(preceding-sibling::p
[contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
concat('|',@name,'|')
)
][1])" />
<xsl:template match=
"p[contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
concat('|',@name,'|')
)]">
<xsl:variable name="vLevel" select="substring(@name,2,1)" />
<xsl:element name="h{$vLevel}">
<xsl:copy-of select="."/>
<xsl:apply-templates select="key('immediate-nodes', generate-id())" />
<xsl:apply-templates select="key('next-headings', generate-id())" />
</xsl:element>
</xsl:template>
<xsl:template match="/*/node()" priority="-20">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
应用于此XML文档(更正了提供的文件并使用name
属性的统一值):
<body>
<p name="h1-title" other="main">Introduction</p>
<p name="h2-title" other="other-h2">XSLT and XQuery</p>
<p name="h3-title" other=" other-h3">XSLT</p>
<p name="">
<p1 name="bold"> XSLT is used to write stylesheets.</p1>
</p>
<p name="h2-title" other="other-h2">XQuery</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h3-title" other="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<p name="h2-title" other=" other-h2">XSLT</p>
</body>
产生了想要的正确结果:
<h1>
<p name="h1-title" other="main">Introduction</p>
<h2>
<p name="h2-title" other="other-h2">XSLT and XQuery</p>
<h3>
<p name="h3-title" other=" other-h3">XSLT</p>
<p name="">
<p1 name="bold"> XSLT is used to write stylesheets.</p1>
</p>
</h3>
</h2>
<h2>
<p name="h2-title" other="other-h2">XQuery</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
<h3>
<p name="h3-title" other="other-h3">XQuery and stylesheets</p>
<p name="">
<p1 name="bold"> XQuery is used to query XML databases.</p1>
</p>
</h3>
</h2>
</h1>
<h1>
<p name="h1-title" other="other-h1">XSLT and XQuery</p>
<h2>
<p name="h2-title" other=" other-h2">XSLT</p>
</h2>
</h1>
请注意:
此转换解决了生成层次结构的主要问题。如果要求顶级name
属性的值为"h-title"
,则只需进行一些微小的更改。
如果需要更多层次结构级别,则只需要将相应的or
子句机械地添加到键的定义中,并使用相应的新值附加管道分隔的所有name
属性值的字符串字符串。
在这里,我调整并重新使用了Jeni Tennison为类似问题提供的解决方案。
答案 2 :(得分:2)
每个分组步骤都将原始元素集作为输入,而您需要每个步骤处理前一个分组步骤生成的组。还有很多其他错误,例如h1-title不是属性名称。
它必须是这样的:
<xsl:for-each-group select="*" group-starting-with="*[@name='h1-title']">
<h1>
<xsl:choose>
<xsl:when test="@name='h1-title'">
<xsl:for-each-group select="current-group()" group-starting-with="*[name='h2-title']">
<xsl:choose>
<h2>
... similar logic for the next level ...
</h2>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</h1>
</xsl:for-each-group>
您可以根据需要深度嵌套,具体取决于您想要处理多少级别;或者如果要处理不确定的数字,可以将代码放在命名模板中并进行递归调用以处理下一级别。在最里层,省略xsl:choose
,只做xsl:copy-of select="current-group()
。
(我刚注意到“name”属性中的尾随空格。如果它们确实存在,你需要将它们包含在比较测试中,或者normalize-space()
去除它们。)