2012-01-11 58 views
0

我有一些XML(XHTML,实际上)文档包含我想自动编号的多级子句;问题在于文档是非结构化的扁平列表,其文本属性中指定的级别。是的,我知道,但我不能切实地改变它们;太多,太大了。XSLT自动编号多级别但非结构化文档

一个简化的情况表示构造和我想的标签是:

<root> 
    <p label="1" class="clause_L1">A</p> 
    <p label="1.1" class="clause_L2">B</p> 
    <p label="1.2" class="clause_L2">C</p> 
    <p label="1.3" class="clause_L2">D</p> 
    <p label="2" class="clause_L1">E</p> 
    <p label="3" class="clause_L1">F</p> 
    <p label="4" class="clause_L1">G</p> 
    <p label="4.1" class="clause_L2">H</p> 
    <p label="4.1.1" class="clause_L3">I</p> 
    <p label="4.1.2" class="clause_L3">J</p> 
    <p label="4.2" class="clause_L2">K</p> 
    <p label="4.3" class="clause_L2">L</p> 
</root> 

经过大量黑客攻击的时候,我已经得到了以下样式:

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

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

<xsl:template match="p[substring(@class, 1, 6) = 'clause']"> 
    <xsl:variable name="class" select="@class"/> 
    <xsl:variable name="level" select="substring(@class, 9, 1)"/> 
    <xsl:variable name="parent" select="preceding-sibling::p[@class = concat('clause_L', $level - 1)][1]"/> 
    <xsl:variable name="parentPos" select="count($parent/preceding-sibling::p[substring(@class, 1, 6) = 'clause']) + 1"/> 

    <clause> 
    <xsl:attribute name="parent"> 
     <xsl:value-of select="$parent/@label"/> 
    </xsl:attribute> 
    <xsl:attribute name="parentPos"> 
     <xsl:value-of select="$parentPos"/> 
    </xsl:attribute> 
    <xsl:attribute name="origLabel"> 
     <xsl:value-of select="@label"/> 
    </xsl:attribute> 
    <xsl:attribute name="label"> 
     <xsl:number count="p[string($parent) = '' or position() &gt; $parentPos][@class = $class]" /> 
    </xsl:attribute> 
    <xsl:value-of select="."/> 
    </clause> 
</xsl:template> 

。 ..给我正确的编号为最低的当前水平:

<clause parent="" parentPos="1" origLabel="1" label="1">A</clause> 
<clause parent="1" parentPos="1" origLabel="1.1" label="1">B</clause> 
<clause parent="1" parentPos="1" origLabel="1.2" label="2">C</clause> 
<clause parent="1" parentPos="1" origLabel="1.3" label="3">D</clause> 
<clause parent="" parentPos="1" origLabel="2" label="2">E</clause> 
<clause parent="" parentPos="1" origLabel="3" label="3">F</clause> 
<clause parent="" parentPos="1" origLabel="4" label="4">G</clause> 
<clause parent="4" parentPos="7" origLabel="4.1" label="1">H</clause> 
<clause parent="4.1" parentPos="8" origLabel="4.1.1" label="1">I</clause> 
<clause parent="4.1" parentPos="8" origLabel="4.1.2" label="2">J</clause> 
<clause parent="4" parentPos="7" origLabel="4.2" label="2">K</clause> 
<clause parent="4" parentPos="7" origLabel="4.3" label="3">L</clause> 

首先,是否有可能为此模板中的标签生成更高级别的数字,因为它看起来像xsl:number只能在当前上下文中使用 - 所以我不知道如何获得当前下一级别加计数器。

其次有没有更好的方法来做到这一点?铭记我坚持使用源数据格式。

回答

0

解决了这个问题,并且使用递归合理地整齐和一般的方式,所以将会处理任意级别。

对于任何试图从平面文档创建结构化文档的人来说,这可以进行有效的修改。

---性能可能是残酷的,但是这不是一个高容量的应用程序,所以我不关心; O)

编辑:性能竟是godawful,所以我已经更新的答案一个更优化的;导致性能问题的实际情况有点奇怪,并且在代码中进行了评论,我认为这与懒惰评估的工作方式有关。如果没有预评估,完整的递归运行需要一分半钟才能运行我的数据(约400条),并且需要5秒。我在.NET 4中使用XSLT引擎。五秒钟听起来有一段时间,但这只是完整的应用程序运行,包括PDF最终文档的生成,下载和浏览器呈现,我没有单独测量转换。

使用源XML如上,XSL是:

<xsl:template match="p[substring(@class, 1, 6) = 'clause']"> 
    <clause> 
    <xsl:attribute name="origLabel"> 
     <xsl:value-of select="@label"/> 
     </xsl:attribute> 
    <xsl:attribute name="label"> 
     <xsl:call-template name="makeLabel"> 
      <xsl:with-param name="node" select="."/> 
     </xsl:call-template> 
    </xsl:attribute> 
    <xsl:value-of select="."/> 
    </clause> 
</xsl:template> 


<xsl:template name="makeLabel"> 
    <xsl:param name="node"/> 

    <xsl:variable name="class" select="$node/@class"/> 
    <xsl:variable name="level" select="substring($class, 9, 1)"/> 
    <xsl:variable name="parent" select="$node/preceding-sibling::p[@class = concat('clause_L', $level - 1)][1]"/> 
    <xsl:variable name="parentPos" select="count($parent/preceding-sibling::p[substring(@class, 1, 6) = 'clause']) + 1"/> 
    <xsl:variable name="myPos" select="count($node/preceding-sibling::p[substring(@class, 1, 6) = 'clause']) + 1"/> 
    <xsl:variable name="precPos" select="number($myPos - $parentPos)"/> 
    <xsl:variable name="topLevel" select="not($parent)"/> 

    <xsl:if test="string($parent) != ''"> 
     <xsl:call-template name="makeLabel"> 
      <xsl:with-param name="node" select="$parent"/> 
     </xsl:call-template> 
    </xsl:if> 

    <xsl:choose> 
    <xsl:when test="$topLevel"> 
     <xsl:value-of select="count($node/preceding-sibling::p[@class = $class]) + 1" /> 
    </xsl:when> 
    <xsl:otherwise> 
     <!-- 
     It is ABSOLUTELY REQUIRED to pre-evaluate $precPos before the count(); without this the request takes over a minute to run; 
     with it, it takes ~5 seconds. 
     --> 
     <xsl:comment><xsl:value-of select="$precPos"/></xsl:comment> 
     <!-- --> 
     <xsl:value-of select="count($node/preceding-sibling::p[position() &lt; $precPos][@class = $class]) + 1" /> 
    </xsl:otherwise> 
    </xsl:choose>  
    <xsl:text>.</xsl:text> 
</xsl:template> 

其中给出预期结果:

<clause origLabel="1" label="1.">A</clause> 
<clause origLabel="1.1" label="1.1.">B</clause> 
<clause origLabel="1.2" label="1.2.">C</clause> 
<clause origLabel="1.3" label="1.3.">D</clause> 
<clause origLabel="2" label="2.">E</clause> 
<clause origLabel="3" label="3.">F</clause> 
<clause origLabel="4" label="4.">G</clause> 
<clause origLabel="4.1" label="4.1.">H</clause> 
<clause origLabel="4.1.1" label="4.1.1.">I</clause> 
<clause origLabel="4.1.2" label="4.1.2.">J</clause> 
<clause origLabel="4.2" label="4.2.">K</clause> 
<clause origLabel="4.3" label="4.3.">L</clause>