3

我正在尝试编写一个计算新值的递归函数。激励我的基本想法是给定大纲的级别,我想计算新的级别,使得数字始终是连续的(也就是说,你不能立即从级别 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/XPath 2。这是我目前所拥有的,但我没有得到正确的结果,我知道问题出在哪里(从输入数据中的第五项开始);我已经包含解释我正在尝试做的事情的评论:

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

任何帮助是极大的赞赏。

4

1 回答 1

5

这是一个简单的解决方案

<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>
于 2013-05-19T04:36:25.077 回答