我正在尝试编写一个计算新值的递归函数。激励我的基本思想是,给定大纲的级别,我想计算新级别,以使数字始终是连续的(也就是说,您不能立即从2级转到5级),但要遵守原始关系。

给定这样的输入(注意输入可能会完全不同):

<root>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="4">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="9">yo</item>
    <item outlinePos="3">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="4">yo</item>
</root>


我想要这个输出

<root>
    <item outlinePos="0">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="0">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="2">yo</item>
</root>


我正在使用XSLT 2 / XPath2。这是到目前为止的结果,但是我没有得到正确的结果,我知道问题出在哪里(从输入数据的第五项开始);我已经添加了评论,解释了我要做什么:

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

<xsl:template match="item[@outlinePos]">
    <xsl:element name="item">
        <xsl:attribute name="outlinePos" select="test:newLevel(.)"/>
        <xsl:apply-templates select="@*[name(.)!='outlinePos'] | node()"/>
    </xsl:element>
</xsl:template>

<xsl:function name="test:newLevel">
    <xsl:param name="context"/>
    <xsl:choose>
        <xsl:when test="not($context/preceding-sibling::item[1])"> <!-- if we don't have any preceding-sibling items, then we know we are at level 0 -->
            <xsl:sequence select="0"/> <!-- start at 0 not 1 -->
        </xsl:when>
        <xsl:when test="$context/@outlinePos > $context/preceding-sibling::item[1]/@outlinePos"> <!-- if the current item is greater than the previous item, then we know it should come immediately after the previous item so find the level for the previous item and increment it by one -->
            <xsl:sequence select="test:newLevel($context/preceding-sibling::item[1])+1"/>
        </xsl:when>
        <xsl:when test="$context/@outlinePos = $context/preceding-sibling::item[1]/@outlinePos"> <!-- if the current item equals the previous item, then we know the levels should be the same, but we need to know the previous level so find it -->
            <xsl:sequence select="test:newLevel($context/preceding-sibling::item[1])"/>
        </xsl:when>
        <xsl:when test="$context/@outlinePos &lt; $context/preceding-sibling::item[1]/@outlinePos"> <!-- if the current item is less than the previous item, then we know the new level will depend on the closest value that is either equal to the current value or less than the current value; if it is the latter, then we need to increment the final result -->

            <xsl:variable name="curOutlinePos" select="$context/@outlinePos"/>

            <!-- the next two variables here is the part that doesn't work... Neither are computing the expected values -->
            <xsl:variable name="positionClosestOutlinePosEquals" select="count($context/preceding-sibling::item[1][(@outlinePos = $curOutlinePos)]/preceding-sibling::item)"/>
            <xsl:variable name="positionClosestOutlinePosLessThan" select="count($context/preceding-sibling::item[1][(@outlinePos &lt; $curOutlinePos)]/preceding-sibling::item)"/>
            <xsl:message>Note Equals: <xsl:value-of select="$positionClosestOutlinePosEquals"/> Less than: <xsl:value-of select="$positionClosestOutlinePosLessThan"/></xsl:message>

            <!-- Once the above variables compute the right values (by selecting the right nodes), then we'll need to update the following  -->
            <xsl:choose>
                <xsl:when test="$positionClosestOutlinePosEquals &lt; $positionClosestOutlinePosLessThan">
                    <xsl:sequence select="test:newLevel($context/preceding-sibling::item[1][(@outlinePos = $curOutlinePos)])"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence select="test:newLevel($context/preceding-sibling::item[1][(@outlinePos &lt; $curOutlinePos)])+1"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="9999998"/> <!-- for testing purposes, 9999998 means "not processed", just have to make sure test data does not use 9999998 as a @outlinePos value -->
        </xsl:otherwise>
    </xsl:choose>
</xsl:function>


任何帮助是极大的赞赏。

最佳答案

这是一个简单的解决方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="@outlinePos">
   <xsl:attribute name="outlinePos" select="my:level(..)"/>
 </xsl:template>

 <xsl:function name="my:level" as="xs:integer">
  <xsl:param name="pElem" as="element()"/>

  <xsl:variable name="vOrigLevel" select="$pElem/@outlinePos/number()"/>
  <xsl:variable name="vPrecedingElem" select=
   "$pElem/preceding-sibling::item[@outlinePos/number() le $vOrigLevel][1]"/>

  <xsl:sequence select=
   "if(not($vPrecedingElem))
      then 0
      else if($vPrecedingElem/@outlinePos/number() lt $vOrigLevel)
       then my:level($vPrecedingElem) +1
       else if($vPrecedingElem/@outlinePos/number() eq $vOrigLevel)
         then my:level($vPrecedingElem)
         else (: Impossible to happen :) -999
   "/>
 </xsl:function>
</xsl:stylesheet>


当此转换应用于提供的XML文档时:

<root>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="4">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="9">yo</item>
    <item outlinePos="3">yo</item>
    <item outlinePos="8">yo</item>
    <item outlinePos="4">yo</item>
</root>


所需的正确结果产生了:

<root>
    <item outlinePos="0">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="0">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="1">yo</item>
    <item outlinePos="2">yo</item>
    <item outlinePos="2">yo</item>
</root>

10-08 19:14