XSLT 2.0 通过元素的第一次出现拆分 current-group()

XSLT 2.0 Splitting current-group() by first occurence of an element

使用 XSLT 2.0,假设您有 current-group() = { A, X, B, B, X } 其中 A, B,X 是元素。在 B 第一次出现时拆分它以获得两个序列 S1S2 这样 S1 = { A, X }S2 = { B, B, X }?是否可以使用 xsl:for-each-group 构造来完成此操作?

编辑:current-group() 的元素不保证是兄弟姐妹,但保证按文档顺序排列。


第一次尝试: 使用 xsl:for-each-groupgroup-starting-with

<xsl:for-each-group select="current-group()" group-starting-with="B[1]">
  <xsl:choose>
    <xsl:when test="position() = 1">
      <!-- S1 := current-group() -->
    </xsl:when>
    <xsl:otherwise>
      <!-- S2 := current-group() -->
    </xsl:otherwise>
  </xsl:choose>
</xsl:for-each-group>

如果 current-group() 的第一个 B 没有前面的同级 B,则此方法有效。 我原以为位置谓词 [1] 会限定在 select 子句中,因为 current-group()[self::B][1] returns 是正确的 B。我很想知道为什么它不以这种方式限定范围。

XML

<root>
  <A>A1</A>
  <B>B1-1</B>
  <B>B1-2</B>
  <A>A2</A>
  <B>B2-1</B>
  <B>B2-2</B>
</root>

XSLT

<xsl:template match="root">
  <xsl:copy>
    <xsl:for-each-group select="*" group-starting-with="A">
      <xsl:for-each-group select="current-group()" group-starting-with="B[1]">
        <xsl:choose>
          <xsl:when test="position() = 1">
            <S1><xsl:copy-of select="current-group()" /></S1>
          </xsl:when>
          <xsl:otherwise>
            <S2><xsl:copy-of select="current-group()" /></S2>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

结果

<root>
  <S1>
    <A>A1</A>
  </S1>
  <S2>
    <B>B1-1</B>
    <B>B1-2</B>
  </S2>
  <S1>
    <A>A2</A>
    <B>B2-1</B>
    <B>B2-2</B>
  </S1>
</root>

如您所见,第一组已正确拆分,但第二组未正确拆分。但是,如果您将 current-group() 包装在父级中,然后将其传递给 select 子句,这将起作用,但这似乎效率低下。

functx 库定义了一个函数 functx:index-of-node (http://www.xsltfunctions.com/xsl/functx_index-of-node.html):

<xsl:function name="functx:index-of-node" as="xs:integer*"
              xmlns:functx="http://www.functx.com">
  <xsl:param name="nodes" as="node()*"/>
  <xsl:param name="nodeToFind" as="node()"/>

  <xsl:sequence select="
  for $seq in (1 to count($nodes))
  return $seq[$nodes[$seq] is $nodeToFind]
 "/>

</xsl:function>

这将减少您对

的第二种方法
<xsl:template match="root">
  <xsl:copy>
    <xsl:for-each-group select="*" group-starting-with="A">
        <xsl:variable name="pos" select="functx:index-of-node(current-group(), (current-group()[self::B])[1])"/>
        <S1>
            <xsl:copy-of select="current-group()[position() lt $pos]"/>
        </S1>
        <S2>
            <xsl:copy-of select="current-group()[position() ge $pos]"/>
        </S2>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

在具有扩展功能的 Saxon 10 PE 或 EE 的“新”“XSLT 4”世界中 saxon:items-before and saxon:items-from and syntax extension for anonymous functions,您可以将其写为

<?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" xmlns:saxon="http://saxon.sf.net/"
    exclude-result-prefixes="#all" version="3.0">

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

    <xsl:output indent="yes"/>

    <xsl:template match="root">
        <xsl:copy>
            <xsl:for-each-group select="*" group-starting-with="A">
                <S1>
                    <xsl:apply-templates
                        select="saxon:items-before(current-group(), .{ . instance of element(B) })"/>
                </S1>
                <S2>
                    <xsl:apply-templates
                        select="saxon:items-from(current-group(), .{ . instance of element(B) })"/>
                </S2>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>