如何使用xslt 2.0保留内容时包装列表

时间:2018-09-05 20:30:03

标签: xslt-2.0 xslt-grouping

我有一个正在使用的测试文件,我正在尝试包装元素,同时保持其余内容不变。我一直在尝试使用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>
    

2 个答案:

答案 0 :(得分:0)

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

您需要调整更多选择。

您的模板仅需应用于listitem,其前身不是listitem(可能是具有相同类的列表项)。在模板内部,您需要打开ol标记,然后使用不同的规则来计算项目和所有后继者的内容,只要它们是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>

答案 1 :(得分:0)

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

<?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声明。