XSL计数在唯一排序节点之前

时间:2013-11-21 20:56:33

标签: xml sorting xslt xpath

我有一个相当复杂的XSL任务。我有一个类似于

的XML文档
<authorlist>
    <orgs>
        <org id="org1" name="Org A"/>
        <org id="org2" name="Org B"/>
        <org id="org3" name="Org C"/>
    </orgs>
    <authors>
        <auth name="C. Thor">
            <affiliations>
                <affil id="org2"/>
                <affil id="org3"/>
            </affiliations>
        </auth>
        <auth name="A. Thor">
            <affiliations>
                <affil id="org3"/>
            </affiliations>
        </auth>
        <auth name="B. Thor">
            <affiliations>
                <affil id="org1"/>
            </affiliations>
        </auth>
    </authors>
</authorlist>

我想写一个XSL转换,它将产生以下(文本)输出

1 Org C
2 Org A
3 Org B

A. Thor ^{1}
B. Thor ^{2}
C. Thor ^{1,3}

也就是说,作者按名称按字母顺序排序。打印每个作者的姓名,以及表明其所属关系的上标。组织按照它们首次出现在作者排序列表中的顺序打印。每位作者可能有多个隶属关系。

以下是我认为我需要做的事情:

  1. 创建一个从组织映射到序数的密钥,以便我可以正确地对组织进行排序(并在作者名称上添加正确的上标)。我相信我知道该怎么做。
  2. 要创建该密钥,我需要计算在与当前(创建密钥时)组织关联的作者的第一个实例之前的唯一作者从属关系的数量。我想我知道怎么做。
  3. 踢球者是如何定义“前面”和“第一”的。如果我理解正确,“前面”和“第一”是由文档顺序定义的,或者可能是由一些模糊的XPath“处理顺序”定义的。我迫切需要通过按名称按字母顺序对作者进行排序来定义“前”和“第一”。我不知道该怎么做,甚至不知道是否可能。
  4. 我可以使用的XSLT处理器是xsltproc,它实现了XSLT 1.0。如果有一个足够引人注目的案例,我可以考虑制作一个不同的处理器,但我可以使用不同的处理器有点怀疑。

    真实案例变得更加复杂,因为有些组织有多个子组织,还有两类组织,成员组织和访客组织,它们打印在单独的列表中并具有独立的订单他们的上标。但是,我认为解决上述问题就足以完成剩下的工作了。

1 个答案:

答案 0 :(得分:1)

一种方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />

  <xsl:variable name="orgIndex">
    <xsl:apply-templates select="//authors/auth" mode="orgIdx">
      <xsl:sort select="@name" />
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:template match="authorlist">
    <xsl:apply-templates select="authors" />
  </xsl:template>

  <xsl:template match="authors">
    <xsl:apply-templates select="auth">
      <xsl:sort select="@name" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="auth">
    <xsl:value-of select="@name" />
    <xsl:text> ^{</xsl:text>
    <xsl:apply-templates select="affiliations/affil" mode="orgIdx">
      <xsl:sort select="string-length(substring-before($orgIndex, @id))" data-type="number" />
    </xsl:apply-templates>
    <xsl:text>}</xsl:text>
    <xsl:if test="position() &lt; last()">
      <xsl:value-of select="'&#xA;'" />
    </xsl:if>
  </xsl:template>

  <xsl:template match="affil" mode="orgIdx">
    <xsl:variable name="str" select="substring-before($orgIndex, @id)" />
    <xsl:variable name="idx" select="string-length($str) - string-length(translate($str, '|', ''))" />
    <xsl:value-of select="$idx" />
    <xsl:if test="position() &lt; last()">,</xsl:if>
  </xsl:template>

  <xsl:template match="auth" mode="orgIdx">
    <xsl:for-each select="affiliations/affil">
      <xsl:value-of select="concat('|', @id)" /> 
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

结果

A. Thor ^{1}
B. Thor ^{2}
C. Thor ^{1,3}

此方法基于以正确的顺序构建affil/@id的分隔字符串(即auth按字母顺序排列,并按{1}}按文档顺序排列。

对于您的示例,字符串auth将为$orgIndex

'|org3|org1|org2|org3'将在该字符串中重复,但这是正确的,因为我们不关心字符串的后部。

现在我们可以使用@id来确定第一次出现ID之前的分隔符字符数,从而产生您似乎正在寻找的数字索引。