我正在尝试编写一个计算新值的递归函数。什么激励我的基本想法是,给定水平的轮廓,我想计算新的水平,使得数字总是连续的(也就是说,你不能立即从第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 < $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 < $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 < $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 < $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>
非常感谢任何帮助。
答案 0 :(得分: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>