2010-06-07 39 views
4

我试图使用XSLT来转换文档,方法是用整数id标记一组XML节点,从0开始,并为组中的每个节点增加一个。传递给样式表的XML应该被回显出来,但是增加了这个额外的信息。如何使用XSLT使用唯一的,连续的,增加的整数ID来标记特定的节点?

只是要清楚什么我说的,这里是如何这种转变将使用DOM表示:

states = document.getElementsByTagName("state"); 
for(i = 0; i < states.length; i++){ 
    states.stateNum = i; 
} 

这是非常简单的使用DOM,但我有更多的麻烦这样做与XSLT。我设计的当前策略是从身份转换开始,然后创建一个全局变量,选择并存储我想要编号的所有节点。然后我创建一个匹配那种节点的模板。这个想法是,在模板中,我会查找匹配节点在全局变量nodelist中的位置,这会给我一个唯一的数字,然后我可以将它设置为一个属性。

这种方法的问题是,位置功能只能与上下文节点使用,所以像下面这样是非法的:

<template match="state"> 
    <variable name="stateId" select="@id"/> 
    <variable name="uniqueStateNum" select="$globalVariable[@id = $stateId]/position()"/> 
</template> 

这同样适用于以下情况:

<template match="state"> 
    <variable name="stateId" select="@id" 
    <variable name="stateNum" select="position($globalVariable[@id = $stateId])/"/> 
</template> 

为了使用position()在$ globalVariable中查找元素的位置,必须更改上下文节点。

我找到了一个解决方案,但它是非常不理想的。基本上,在模板中,我使用for-each遍历全局变量。 For-each改变上下文节点,所以这允许我以我描述的方式使用position()。问题是,这将通常将O(n)操作转换为O(n^2)操作,其中n是节点列表的长度,因为无论何时匹配模板,都需要遍历整个列表。我认为必须有一个更优雅的解决方案。

总之,这里是我目前(略简体)XSLT样式表:

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:s="http://www.w3.org/2005/07/scxml" 
    xmlns="http://www.w3.org/2005/07/scxml" 
    xmlns:c="http://msdl.cs.mcgill.ca/" 
    version="1.0"> 
    <xsl:output method="xml"/> 

    <!-- we copy them, so that we can use their positions as identifiers --> 
    <xsl:variable name="states" select="//s:state" /> 


    <!-- identity transform --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="s:state"> 

     <xsl:variable name="stateId"> 
      <xsl:value-of select="@id"/> 
     </xsl:variable> 

     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 

      <xsl:for-each select="$states"> 
       <xsl:if test="@id = $stateId"> 
        <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
         <xsl:value-of select="position()"/> 
        </xsl:attribute> 
       </xsl:if> 
      </xsl:for-each> 

      <xsl:apply-templates select="node()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

我会很感激任何意见,任何人都可以提供。谢谢。

+0

好问题(+1)。看到我的答案是一个非常简单但正确的解决方案。:) – 2010-06-07 23:08:42

回答

3

该转化

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:s="http://www.w3.org/2005/07/scxml" 
> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="node()|@*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="s:state"> 
    <xsl:variable name="vNum"> 
    <xsl:number level="any" count="s:state"/> 
    </xsl:variable> 

    <xsl:copy> 
    <xsl:copy-of select="@*"/> 

    <xsl:attribute name="stateId"> 
    <xsl:value-of select="@id"/> 
    </xsl:attribute> 

    <xsl:attribute name="id"> 
    <xsl:value-of select="$vNum -1"/> 
    </xsl:attribute> 

    <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

当所提供的XML文档施加:

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state id="Compound1"> 
     <state id="Basic1"/> 
     <state id="Basic2"/> 
     <state id="Basic3"/> 
    </state> 
</scxml> 

产生想要的,正确的输出

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state stateId="Compound1" id="0"> 
     <state stateId="Basic1" id="1"/> 
     <state stateId="Basic2" id="2"/> 
     <state stateId="Basic3" id="3"/> 
    </state> 
</scxml> 
+0

完美的作品,谢谢! – jbeard4 2010-06-08 00:36:00

0

最简单的方法:

<xsl:template match="s:state"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
     <xsl:value-of select="count(preceding::s:state)" /> 
    </xsl:attribute> 
    <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
</xsl:template> 

不知道你的XSLT处理器如何处理preceding轴,所以这是值得基准在任何情况下。

+0

Tomalak,基于我对前面的轴的理解,这看起来像是一个合理的方法。不幸的是,它似乎并不奏效。请参见下面的XML文档: \t <状态ID = “Compound1”> \t \t <状态ID =“Basic1 “/> \t \t <状态ID =” Basic2 “/> \t \t <状态ID =” Basic3" /> \t 这将创建以下状态号: STATEID:Compound1,stateNum:0 ; stateId:Basic1,stateNum:0; stateId:Basic2,stateNum:1; stateId:Basic3,stateNum:2 检查Xalan,4xslt和xsltproc。 任何想法? – jbeard4 2010-06-07 20:38:03

+0

嗯。我明白你的意思了。尝试添加'count(ancestor :: s:state)'和'count(之前的:: s:state)',但这也可能是错误的。现在没有人需要测试,我现在正在使用我的手机。 ;-) – Tomalak 2010-06-07 20:49:40

相关问题