简体   繁体   English

使用XSLT 1.0通过分组来循环XML节点

[英]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>

Added: 添加:

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 the chunk 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM