2013-11-21 39 views
3

我有一个相当复杂的XSL任务。我有一个看起来像XSL计数前面唯一排序的节点

<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> 

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

1 Org C 
2 Org A 
3 Org B 

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

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

这里就是我想我需要做的:

  1. 创建自组织映射到序数,这样我就可以正确的组织进行排序(并把正确的标上作者姓名)的关键。我相信我知道如何做到这一点。
  2. 要创建该密钥,我需要计算与当前(创建密钥时)组织关联的作者的第一个实例之前的唯一作者关联的数量。我想我知道该怎么做。
  3. 踢球者是如何定义“之前”和“第一”的。如果我理解正确,则“之前”和“第一个”由文档顺序定义,或者可能由一些模糊的XPath“处理顺序”定义。我非常需要通过按名称字母顺序排列作者来定义“之前”和“第一”。我不知道如何做到这一点,甚至是否有可能。

我可以使用的XSLT处理器是xsltproc,它实现了XSLT 1.0。如果有足够引人注目的情况,我可以考虑制作一个不同的处理器,但有些疑问我可以使用不同的处理器。

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

+0

你能解释一下究竟是怎样的A. Thor^{1}'汇合在一起?代表什么是'1',我不知道我明白。 (我猜它指的是'org3',因为如果你按名称排序作者,那将是第一个组织,对吗?) – Tomalak

+0

A. Thor与org3(Org C)有联系。由于A. Thor是第一个(按字母顺序排列的)作者,而org3是A. Thor的第一个隶属关系,所以org3的索引为1.因此,我们有A. Thor^{1},表明A. Thor与索引有关联1,这是组织C.这很混乱,这可能是我为什么挣扎着。 – jsw

+0

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() &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是按照字母顺序,并按文件顺序在auth内)。

对于您的示例字符串$orgIndex将是'|org3|org1|org2|org3'

@id s将在该字符串中重复,但这是完全正确的,因为我们不关心字符串的后部。

现在我们可以使用substring-before()来确定第一次出现ID之前的分隔字符数,这会导致您似乎正在寻找的数字索引。

+0

这是丑陋的,但它的工作原理,所以我不在乎它有多丑。需要进行一项更改:xsl:sort需要额外的属性data-type =“number”。 – jsw

+1

机会是没有真正美丽的解决方案,这个特殊的问题。 – Tomalak

+0

Crud。这有一个问题,即如果字符串'$ orgIndex'是''| org3 | org3 | org1 | org2 | org3',那么'org1'将被赋予索引3和''org2'索引4。我的真实世界作者名单约有60个组织和400个人,组织指数最多为488! 我正在寻找解决方案。此外,还有一些组织的“id”是另一个组织的“id”的子字符串(例如“org1”和“org10”)。我认为这里的解决方案很简单,只需在'id'的相应末尾添加一个分隔符。 – jsw