Writing a recursive function XSLT/XPath for computing new values -
i trying write recursive function computes new values. basic idea of motivates me given levels of outline, want compute new levels such numbers sequential (that is, can't go level 2 level 5) while respecting original relationships.
given input (note inputs quite different):
<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>
i want output
<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>
i using xslt 2/xpath 2. have far not getting right results, know problem (begins fifth item in input data); i've included comments explaining i'm trying do:
<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 don't have preceding-sibling items, know @ level 0 --> <xsl:sequence select="0"/> <!-- start @ 0 not 1 --> </xsl:when> <xsl:when test="$context/@outlinepos > $context/preceding-sibling::item[1]/@outlinepos"> <!-- if current item greater previous item, know should come after previous item find level previous item , increment 1 --> <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 current item equals previous item, know levels should same, need know previous level find --> <xsl:sequence select="test:newlevel($context/preceding-sibling::item[1])"/> </xsl:when> <xsl:when test="$context/@outlinepos < $context/preceding-sibling::item[1]/@outlinepos"> <!-- if current item less previous item, know new level depend on closest value either equal current value or less current value; if latter, need increment final result --> <xsl:variable name="curoutlinepos" select="$context/@outlinepos"/> <!-- next 2 variables here part doesn't work... neither computing 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 above variables compute right values (by selecting right nodes), we'll need update 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"/> <!-- testing purposes, 9999998 means "not processed", have make sure test data not use 9999998 @outlinepos value --> </xsl:otherwise> </xsl:choose> </xsl:function>
any appreciated.
here simple solution:
<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)) 0 else if($vprecedingelem/@outlinepos/number() lt $voriglevel) my:level($vprecedingelem) +1 else if($vprecedingelem/@outlinepos/number() eq $voriglevel) my:level($vprecedingelem) else (: impossible happen :) -999 "/> </xsl:function> </xsl:stylesheet>
when transformation applied provided xml document:
<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>
the wanted, correct result produced:
<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>
Comments
Post a Comment