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 を使用しています。これはこれまでのところですが、正しい結果が得られていません。問題がどこにあるかはわかっています (入力データの 5 番目の項目から始まります)。私がやろうとしていることを説明するコメントを含めました:

<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 に答える