【问题标题】:How to wrap a list while preserving content with xslt 2.0如何在使用 xslt 2.0 保留内容的同时包装列表
【发布时间】:2018-09-05 20:30:02
【问题描述】:

我有一个我一直在玩的测试文件,我正在尝试包装 元素,同时保持其余内容不变。我一直在尝试使用 for-each-group,但它将每个列表项包装在

    中。如果我将模板匹配更改为 root ,我可以让它工作,但我会失去其他一切。有什么想法吗?提前致谢。这是我目前拥有的 xml 和 xsl。

xml 输入

<?xml version="1.0" encoding="UTF-8"?>
<document>
<chapter>
<title toc_id="new">An new story</title>
<para>New stories are different</para>
<listitem class="ol">Here is one list item</listitem>
<listitem class="ol">Here is two list item</listitem>
<listitem class="ol">Here is three list item</listitem>
<section>
<title toc_id="h2-intro-1">Introduction H1</title>
<para>Upon a time ....</para>
<title toc_id="h2-intro-2">Introduction H2</title>
<para>... there was a young prince. http://www.stuff.com, here's a website</para>
</section>
</chapter>
</document>

对于 xslt:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" 
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="listitem">
<xsl:for-each-group select="." group-by="@class">
<ol>
<xsl:for-each select=".">
<li>
<p>
<xsl:value-of select="."/>
</p>
</li>
</xsl:for-each>
</ol>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

我正在寻找的输出是这样的:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<document>
<chapter>
<title toc_id="new">An new story</title>
<para>New stories are different</para>
<ol>
<li>
<p>Here is one list item</p>
</li>
<li>
<p>Here is two list item</p>
</li>
<li>
<p>Here is three list item</p>
</li>
</ol>
<section>
<title toc_id="h2-intro-1">Introduction H1</title>
<para>Upon a time ....</para>
<title toc_id="h2-intro-2">Introduction H2</title>
<para>... there was a young prince. http://www.stuff.com, here's a website</para>
</section>
</chapter>
</document>

【问题讨论】:

  • 那么您需要将相邻的listitem 元素包装在一起还是只包装具有特定class 属性或相同类的那些?是否也可以有混合内容,即元素节点之间的非空白文本节点?一般来说,这似乎是match="*[listitem]" 上的一项任务,然后是for-each-group group-adjacent="boolean(self::listitem)" 可能还有一个嵌套的for-each-group group-by="@class"
  • 不,我试图弄清楚如何包装相似的元素,但无法完全理解 for-each-group 的工作原理。

标签: xslt-2.0 xslt-grouping


【解决方案1】:

因为listitem 的模板匹配每一项,而您的fo-each-group 仅适用于.(当前元素),因此会包装每个元素。

您需要调整更多选择。

您的模板只需应用于listitem,其前身不是listitem(可能是同一个类的listitem)。在模板里面你需要打开ol标签,然后使用不同的规则来输入item的内容和所有的后继,只要是listitem即可。

检查 XPath 轴:https://www.w3schools.com/xml/xpath_axes.asp

应该可以这样做:

<xsl:template match="listitem[preceding-sibling::self[position()=1 and name() != 'listitem']]">
    <ol>
    <xsl:apply-templates mode="inlist" select="."/>
    </ol>
</xsl:template>
<xsl:template match="listitem" mode="inlist">
    <li><xsl:value-of select="."/></li>
    <xsl:apply-templates  select="following-sibling::self[position() = 1 and name() = 'listitem']" mode="inlist"/>
</xsl:template>

【讨论】:

  • 我知道我的比赛有问题!感谢您解释缺少的内容。
【解决方案2】:

我会使用match="*[listitem]"match="*[listitem[@class]]" 来匹配包含要分组/包装的listitem 元素的父元素,然后在父元素的子元素上使用for-each-group,如果你只期望它们作为元素,您可以在 XSLT 3 中使用 select="*" composite="yes" group-adjacent="boolean(self::listitem), @class" 来识别具有相同 class 属性值的相邻 listitem

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="html" indent="yes" html-version="5"/>

  <xsl:template match="*[listitem]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:for-each-group select="*" composite="yes" group-adjacent="boolean(self::listitem), @class">
              <xsl:choose>
                  <xsl:when test="current-grouping-key()[1]">
                      <xsl:element name="{current-grouping-key()[2]}">
                          <xsl:apply-templates select="current-group()"/>
                      </xsl:element>
                  </xsl:when>
                  <xsl:otherwise>
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:otherwise>
              </xsl:choose>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="listitem">
      <li>
          <p>
              <xsl:apply-templates/>
          </p>
      </li>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gWmuiJK

使用 XSLT 2,您可以使用两个嵌套的 for-each-group,例如

      <xsl:for-each-group select="*" group-adjacent="boolean(self::listitem)">
          <xsl:choose>
              <xsl:when test="current-grouping-key()">
                  <xsl:for-each-group select="current-group()" group-adjacent="@class">
                      <xsl:element name="{current-grouping-key()}">
                          <xsl:apply-templates select="current-group()"/>
                      </xsl:element>                          
                  </xsl:for-each-group>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>

请参阅 https://xsltfiddle.liberty-development.net/gWmuiJK/1,对于完整的 XSLT 2 解决方案,您当然需要将 xsl:mode 声明替换为身份转换模板(您已在发布的代码中)。

【讨论】:

  • 很棒的答案!这极大地帮助我了解了我对每个组的缺失。
猜你喜欢
  • 1970-01-01
  • 2016-05-27
  • 1970-01-01
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 2017-02-17
相关资源
最近更新 更多