编写用于计算新值的递归函数XSLT/XPath

编写用于计算新值的递归函数XSLT/XPath,xslt,xslt-2.0,Xslt,Xslt 2.0,我试图编写一个递归函数来计算新值。激励我的基本思想是,给定大纲的级别,我想计算新的级别,使数字始终是连续的(也就是说,你不能立即从2级转到5级),但同时尊重原始关系 给定这样的输入(注意,输入可能会非常不同): 哟 哟 哟 哟 哟 哟 哟 哟 哟 哟 哟 我想要这个输出 <root> <item outlinePos="0">yo</item> <item outlinePos="1">yo</item> &

我试图编写一个递归函数来计算新值。激励我的基本思想是,给定大纲的级别,我想计算新的级别,使数字始终是连续的(也就是说,你不能立即从2级转到5级),但同时尊重原始关系

给定这样的输入(注意,输入可能会非常不同):


哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
我想要这个输出

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

哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
我使用的是XSLT2/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>

哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
生成所需的正确结果:

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

哟
哟
哟
哟
哟
哟
哟
哟
哟
哟
哟

这就是简单性本身。非常感谢。