唯一排序节点之前的 XSL 计数
Posted
技术标签:
【中文标题】唯一排序节点之前的 XSL 计数【英文标题】:XSL count preceding unique sorted nodes 【发布时间】:2013-12-06 13:20:52 【问题描述】:我有一个相当复杂的 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
也就是说,作者按姓名字母顺序排序。每个作者的名字都被打印出来,以及表明她的隶属关系的上标。组织按照它们在作者排序列表中首次出现的顺序打印。每个作者可能有多个隶属关系。
这是我认为我需要做的:
-
创建一个从组织映射到序数的键,以便我可以正确地对组织进行排序(并将正确的上标放在作者姓名上)。我相信我知道该怎么做。
要创建该密钥,我需要计算与当前(创建密钥时)组织有关联的作者的第一个实例之前的唯一作者隶属关系的数量。我想我知道该怎么做。
关键是如何定义“在前”和“在前”。如果我理解正确,“preceding”和“first”是由文档顺序定义的,或者可能是由一些模糊的 XPath“处理顺序”定义的。我非常需要通过按作者姓名字母顺序对作者进行排序来定义“在先”和“第一”。我不知道如何做到这一点,甚至不知道这是否可能。
我可以使用的 XSLT 处理器是 xsltproc,它实现了 XSLT 1.0。如果有足够令人信服的案例,我可以考虑提供不同的处理器,但我能否使用不同的处理器有点怀疑。
现实世界的情况会变得更加复杂,因为有些组织有多个子组织,而且还有两类组织,成员组织和访客组织,它们打印在单独的列表中,并且具有独立的顺序他们的上标。但是,我认为解决上述问题就足够了。
【问题讨论】:
您能解释一下A. Thor ^1
究竟是如何组合在一起的吗? 1
代表什么,我不确定我是否理解。 (我猜它指的是org3
,因为如果您按姓名对作者进行排序,那将是第一个组织。对吧?)
A. Thor 隶属于 org3 (Org C)。由于 A. Thor 是第一个(按字母顺序)作者,而 org3 是 A. Thor 的第一个隶属关系,所以 org3 获得索引 1。所以,我们有 A. Thor ^1,表明 A. Thor 隶属于具有索引的组织1,即 Org C。这很令人困惑,这可能是我为之苦苦挣扎的原因。
组织 C 是如何获得索引 1 的?您的示例中没有任何内容暗示这一点。另外,到目前为止,您尝试过什么?请发布您现有的作品并解释为什么它不能满足您的需求。
Org C 的索引为 1,因为它是第一作者的第一个单位。正如我在原文中所说,“组织按照它们首先出现在作者排序列表中的顺序打印。”我应该更清楚地说明,“组织按照它们在作者排序列表中首次出现的顺序进行索引,并且按照它们的索引顺序打印。”
【参考方案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() < last()">
<xsl:value-of select="'
'" />
</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() < 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. 雷神 ^1 B. 雷神 ^2 C. 雷神 ^1,3此方法基于以正确的顺序(即按名称字母顺序按auth
,按文档顺序在auth
内)构建一个分隔字符串affil/@id
。
对于您的示例,字符串 $orgIndex
将是 '|org3|org1|org2|org3'
。
@id
s 将在该字符串中重复,但这没关系,因为我们不关心字符串的后部。
现在我们可以使用substring-before()
来确定第一次出现 ID 之前的分隔字符数,这会导致您似乎正在寻找数字索引。
【讨论】:
这很丑,但它有效,所以我不在乎它有多丑。需要进行一项更改:xsl:sort 需要一个附加属性 data-type="number"。 这个特殊问题可能没有真正漂亮的解决方案。 粗鲁。这有一个问题,即如果字符串$orgIndex
是'|org3|org3|org1|org2|org3'
,那么'org1'
将被赋予索引3 和'org2'
索引4。我的真实世界作者列表大约有60 个组织和400 人,并且组织指数最多为 488!我正在努力寻找解决方案。此外,有些组织的id
是另一个组织的id
的子字符串(例如'org1'
和'org10'
)。不过,我认为那里的解决方案很简单,只需在id
的适当末尾添加一个分隔符即可。以上是关于唯一排序节点之前的 XSL 计数的主要内容,如果未能解决你的问题,请参考以下文章