Re: [xsl] Define internal xsl variable

Subject: Re: [xsl] Define internal xsl variable
From: Mukul Gandhi <gandhi.mukul@xxxxxxxxx>
Date: Fri, 7 Oct 2011 20:02:46 +0530
I played with little bit of schema awareness for this use case, and
here's what I came up with,

(this is essentially produced with Mike's idea to have recursive
processing of your input. I've used a recursive named template,
where's Mike suggested to use sibling recursion -- which is actually
elegant, but conceptually both achieve same objectives)

Firstly, I could analyze why we need recursion for this problem.
Essentially because, your further output depends on the output
generated so far.

With these thoughts, here's a solution simulating your problem,

Input XML document:

<X xmlns="http://www.example.org/test";
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
xsi:schemaLocation="http://www.example.org/test foo.xsd">
   <cell>foo</cell>
   <cell span="2">bar</cell>
   <cell span="3">baz</cell>
   <cell>test</cell>
</X>

The XSD schema I use for schema aware system (named foo.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema";
targetNamespace="http://www.example.org/test";
xmlns:t="http://www.example.org/test";>

	<xs:element name="X">
	     <xs:complexType>
		  <xs:sequence>
		       <xs:element ref="t:cell" maxOccurs="unbounded"/>
		  </xs:sequence>
	     </xs:complexType>
	</xs:element>

	<xs:element name="cell">
	     <xs:complexType>
		  <xs:simpleContent>
		       <xs:extension base="xs:string">
			    <xs:attribute name="span" type="xs:integer"/>
	               </xs:extension>
		  </xs:simpleContent>
	     </xs:complexType>
	</xs:element>

</xs:schema>

The XSLT 2.0 stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                       xmlns:xs="http://www.w3.org/2001/XMLSchema";
		       xmlns:t="http://www.example.org/test";
exclude-result-prefixes="xs" version="2.0">

          <xsl:output method="xml" indent="yes"/>

	 <xsl:import-schema namespace="http://www.example.org/test";
schema-location="foo.xsd"/>

	 <xsl:template match="t:X">
	      <result>
                  <xsl:call-template name="transformInputStruct">
                       <xsl:with-param name="refNodePos" select="1"
as="xs:integer"/>
                       <xsl:with-param name="resltList" select="1"
as="xs:integer*"/>
                  </xsl:call-template>
	      </result>
	 </xsl:template>

        <xsl:template name="transformInputStruct">
             <xsl:param name="refNodePos" as="xs:integer"/>
             <xsl:param name="resltList" as="xs:integer*"/>

             <t:data><xsl:value-of select="$resltList"/></t:data>
             <xsl:variable name="nxtNode" select="t:cell[$refNodePos + 1]"/>
             <xsl:if test="$nxtNode">
                   <xsl:variable name="previousValue"
select="max($resltList)"/>
                   <xsl:variable name="maxValue" select="if
($nxtNode/@span) then ($previousValue + 1 + $nxtNode/@span) else
($previousValue + 1)"/>
                   <xsl:call-template name="transformInputStruct">
                        <xsl:with-param name="refNodePos"
select="$refNodePos + 1" as="xs:integer"/>
                        <xsl:with-param name="resltList" select="for
$num in ($previousValue + 1) to $maxValue return $num"
as="xs:integer*"/>
                   </xsl:call-template>
              </xsl:if>
         </xsl:template>

</xsl:stylesheet>

When I transform the input document specified above, with the
stylesheet above, I get output:

(the element "result" is a wrapper for your desired result element
sequence -- I wanted to make the output XML document well-formed)

<?xml version="1.0" encoding="UTF-8"?>
<result xmlns:t="http://www.example.org/test";>
     <t:data>1</t:data>
     <t:data>2 3 4</t:data>
     <t:data>5 6 7 8</t:data>
     <t:data>9</t:data>
</result>

I wrote this schema aware solution, to avoid explicit casts in XPath
expressions. To use something like this, invocation of XSLT 2.0
transform must be preceded by an XSD validation of input document.

We could easily have written an untyped version of this solution (i.e
without the schema), but then I would have found it uglier (!) with
the XPath expressions that I might have written for such a solution,
and have lost other benefits of a strongly typed data.

I'm also not suggesting, to over complicate the solution with
unnecessary semantics (i.e schema awareness in this case). But this
solution was just food for some thought :)


On Wed, Oct 5, 2011 at 1:31 PM, Susanne Wunsch <xsl@xxxxxxxxxxxxxxxxx> wrote:
> Hello,
>
> I'm Susanne and did already some xsl transformations before. For now I try
> to define an internal xsl variable but the xpath expression doesn't work
> so far.
>
> (The current xsl approach is shown below.)
>
> The 'origin' variable should be mapped into the following node set (in the
> 'spannedCells' variable):
>
> <t:data>1</t:data>
> <t:data>2 3</t:data>
> <t:data>4 5 6</t:data>
> <t:data>7</t:data>
>
> It shows occupied cells including the "spanned" ones. I think that some
> recursive design should be possible but I just didn't find how to start in
> this case.
>
> I would be very glad about any hint.
>
> Full xsl file follows:
> -------------------------------------------------------------------------
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> B xmlns:xs="http://www.w3.org/2001/XMLSchema";
> B xmlns:t="http://www.example.org/test";
> B version="2.0">
>
> B <xsl:template name="main">
> B  B <xsl:variable name="origin" as="node()*">
> B  B  B  <t:cell>foo</t:cell>
> B  B  B  <t:cell span="2">bar</t:cell>
> B  B  B  <t:cell span="3">baz</t:cell>
> B  B  B  <t:cell>test</t:cell>
> B  B </xsl:variable>
>
> B  B <xsl:variable name="SpannedCells" as="node()*">
> B  B  B  <t:data>1</t:data>
> B  B  B  <xsl:for-each select="$origin[position() > 1]">
> B  B  B  B  <xsl:variable name="PreviousValue"
> B  B  B  B  B  select="xs:integer(max(preceding::t:data/text()))"/>
> B  B  B  B  <xsl:variable name="MaxValue"
> B  B  B  B  B  select="if (./@span) then ($PreviousValue + 1 + @span)
> B  B  B  B  B  else ($PreviousValue + 1)"/>
> B  B  B  B  <t:data>
> B  B  B  B  B  <xsl:value-of select="($PreviousValue + 1) to $MaxValue"/>
> B  B  B  B  </t:data>
> B  B  B  </xsl:for-each>
> B  B  </xsl:variable>
>
> B  B  <xsl:message select="$SpannedCells"/>
> B </xsl:template>
> </xsl:stylesheet>
> ------------------------------------------------------------------------
>
> Thanks and kind regards...
> Susanne





--
Regards,
Mukul Gandhi

Current Thread