[英]Loop XML nodes with grouping using XSLT 1.0
With the following XML 使用以下XML
<page>
<chunk id="1001" sequence="1">
<meta>Inline</meta>
<body>Inline chunk 1</body>
</chunk>
<chunk id="1002" sequence="2">
<meta>Tabs</meta>
<body>Tab chunk 1</body>
</chunk>
<chunk id="1054" sequence="3">
<meta>Tabs</meta>
<body>Tab chunk 2</body>
</chunk>
<chunk id="1023" sequence="4">
<meta>Inline</meta>
<body>Inline chunk 2</body>
</chunk>
<chunk id="1013" sequence="5">
<meta>Tabs</meta>
<body>Tab chunk 3</body>
</chunk>
<chunk id="1072" sequence="6">
<meta>Tabs</meta>
<body>Tab chunk 4</body>
</chunk>
</page>
I would like to apply an XSL template to output this: 我想应用XSL模板来输出以下内容:
<main>
<section class="Inline>
<div>Inline chunk 1</div>
</section>
<section class="Tabs>
<div>Tab chunk 1</div>
<div>Tab chunk 2</div>
</section>
<section class="Inline>
<div>Inline chunk 2</div>
</section>
<section class="Tabs>
<div>Tab chunk 3</div>
<div>Tab chunk 4</div>
</section>
</main>
Basically I would like to output the chunk
in the order of their ./@sequence
, but group them based on their ./meta
value when possible. 基本上,我想按
./@sequence
的顺序输出chunk
,但在可能时根据其./meta
值对它们进行./meta
。 By "possible", I mean only group them if they have the same ./meta
value and next to each other in sequence. “可能”是指仅将它们具有相同的
./meta
值并按顺序排列在一起。
Using Muenchian Method, I could group the chunk
by ./meta
, but only get the following result: 使用Muenchian方法,我可以将
chunk
按./meta
,但只能得到以下结果:
<main>
<section class="Inline>
<div>Inline chunk 1</div>
<div>Inline chunk 2</div>
</section>
<section class="Tabs>
<div>Tab chunk 1</div>
<div>Tab chunk 2</div>
<div>Tab chunk 3</div>
<div>Tab chunk 4</div>
</section>
</main>
This is the XSLT I use: 这是我使用的XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kBucketByLabel" match="/page/chunk" use="./meta" />
<!-- Then we override/define a template to match "/SAM" and write our body HTML here. -->
<xsl:template match="/page" mode="body">
<main>
<xsl:apply-templates select="chunk[generate-id() = generate-id( key('kBucketByLabel', ./meta)[1] )]" mode="bucket">
<xsl:sort select="@sequence" data-type="number" />
</xsl:apply-templates>
</main>
</xsl:template>
<xsl:template match="chunk" mode="bucket">
<xsl:variable name="bucket" select="./meta" />
<section class="{$bucket}">
<xsl:for-each select="key('kBucketByLabel', $bucket)">
<div><xsl:value-of select="./body"/></div>
</xsl:for-each>
</section>
</xsl:template>
</xsl:stylesheet>
One way to do this is through a technique known as "sibling recursion": 一种实现方法是通过称为“同级递归”的技术:
XSLT 1.0 XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/page">
<main>
<xsl:apply-templates select="chunk[not(meta = preceding-sibling::chunk[1]/meta)]"/>
</main>
</xsl:template>
<xsl:template match="chunk">
<section class="{meta}">
<xsl:apply-templates select="." mode="collect"/>
</section>
</xsl:template>
<xsl:template match="chunk" mode="collect">
<div>
<xsl:value-of select="body"/>
</div>
<xsl:apply-templates select="following-sibling::chunk[1][meta = current()/meta]" mode="collect"/>
</xsl:template>
</xsl:stylesheet>
Does this work with an unsorted chunks?
这可用于未分类的块吗? In my sample data above, I intentionally sorted them by
@sequence
for ease of visualization, but my actual data are unsorted.在上面的示例数据中,我有意按
@sequence
对它们进行@sequence
,以便于可视化,但我的实际数据未排序。
......
I managed to have them sorted by first looping through thechunk
s with<xsl:for-each>
, sort them by@sequence
and store copy of them to a RTF.我设法通过首先循环使用
<xsl:for-each>
chunk
对它们进行排序,按@sequence
对其进行@sequence
,并将其副本存储到RTF。 I had to use extension to convert the RTF into node-set and apply your technique.我不得不使用扩展将RTF转换为节点集并应用您的技术。 It works, but I am hoping not to do that if possible.
它有效,但我希望尽可能不要这样做。
Actually, that's exactly what I would do: sort the data first, convert it a node-set, then apply the above method in a second pass. 实际上,这正是我要做的:首先对数据进行排序,将其转换为节点集,然后在第二遍中应用上述方法。
If you don't want to that, you could use a key to refer to the previous/next chunk in sequence - provided that the sequence is contiguous: 如果您不想这样做,则可以使用键来引用序列中的上一个/下一个块,前提是该序列是连续的:
XSLT 1.0 XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="chunk" match="chunk" use="@sequence" />
<xsl:template match="/page">
<main>
<xsl:apply-templates select="chunk[not(meta = key('chunk', @sequence - 1)/meta)]">
<xsl:sort select="@sequence" data-type="number" order="ascending"/>
</xsl:apply-templates>
</main>
</xsl:template>
<xsl:template match="chunk">
<section class="{meta}">
<xsl:apply-templates select="." mode="collect"/>
</section>
</xsl:template>
<xsl:template match="chunk" mode="collect">
<div>
<xsl:value-of select="body"/>
</div>
<xsl:apply-templates select="key('chunk', @sequence + 1)[meta = current()/meta]" mode="collect"/>
</xsl:template>
</xsl:stylesheet>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.