views:

2166

answers:

3

Hello all,

I want to dynamically create variables with dynamic names for later use in my transform, but to do this I'd need to dynamically generate XSL and then run it in the same script.

This is just a rough pseudo code example of what I'm looking for.

      <xsl:for-each select="//constants/constant" >
        <xsl:variable >
            <xsl:attribute name="name">
              <xsl:value-of select="@name"/>
            </xsl:attribute>
          <xsl:attribute name="select">
            <xsl:value-of select="@value"/>
          </xsl:attribute>
        </xsl:variable>
      </xsl:for-each>

Can I use XSL to dynamically build XSL to be run later in the same script?

Note: our XML is transformed via a batch process running a CL XSL transform engine; so just referencing an XSL stylesheet in the XSL document isn't an option.

+2  A: 

XSLT has a special built-in feature that supports generating output, which is XSLT itself.

This is the <xsl:namespace-alias> XSLT directive.

As explaiened by the XSLT 1.0 Spec.:

"

<!-- Category: top-level-element -->
<xsl:namespace-alias
  stylesheet-prefix = prefix | "#default"
  result-prefix = prefix | "#default" />

A stylesheet can use the xsl:namespace-alias element to declare that one namespace URI is an alias for another namespace URI. When a literal namespace URI has been declared to be an alias for another namespace URI, then the namespace URI in the result tree will be the namespace URI that the literal namespace URI is an alias for, instead of the literal namespace URI itself. The xsl:namespace-alias element declares that the namespace URI bound to the prefix specified by the stylesheet-prefix attribute is an alias for the namespace URI bound to the prefix specified by the result-prefix attribute. Thus, the stylesheet-prefix attribute specifies the namespace URI that will appear in the stylesheet, and the result-prefix attribute specifies the corresponding namespace URI that will appear in the result tree. "

Here is a small example of a transformation that generates an xsl:stylesheet containing an xsl:variable, which is constructed in the wanted way:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xxx="my:dummyNS" exclude-result-prefixes="xxx"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:namespace-alias result-prefix="xsl" stylesheet-prefix="xxx"/>

 <xsl:template match="/*">
  <xxx:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xxx:variable name="{@name}">
    <xsl:value-of select="."/>
  </xxx:variable>
 </xxx:stylesheet>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

    <v name="myVarName">myValue</v>

the wanted result is produced:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="myVarName">myValue</xsl:variable>
</xsl:stylesheet>

Then the next step will be to launch in your "script" this dynamically generated XSLT transformation.

Dimitre Novatchev
+1  A: 

Hi Dimitre,

First of all, thanks for your terrific response; I learned quite a lot from it. My XSL is at an intermediate level at best; your pointer was a real help, where many google searches failed me.

My follow up question is this; is it possible to execute my dynamically generated XSL after generating it in the same script at runtime? I know I could just dump the output to another XSL stylesheet and then run another XSL transform; but that would require a significant reworking of our batch script and I want to see what my options are.

To give a similar example of what I'm trying to accomplish: if I was doing something similar in Javascript for example; I could simply generate the Javascript to declare my variables in a string and then call eval('var SomeJSVariable1 = "example"') and I would now have a variable available to me called SomeJSVariable1. This type of functionality is what I'm trying to achieve in XSL.

Here's an example of what I have so far

input xml:

<resgroup id="constants" >
   <resbundle>
     <res id="var1">var1Value</res>
   </resbundle>
   <resbundle>
    <res id="var2">var2Value</res>
   </resbundle>
</resgroup>

XSL:

 <xsl:stylesheet
   version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:metaxsl="my:metaxsl"
   exclude-result-prefixes="metaxsl"

  <xsl:namespace-alias result-prefix="xsl" stylesheet-prefix="metaxsl" />


  <xsl:template name="BuildInstantiateConstantsTemplate_tmpl">
    <metaxsl:template match="/" name="instantiateConstants_tmpl">
      <metaxsl:template>
        <xsl:for-each select="//resgroup[@id='constants']/resbundle/res">
          <metaxsl:variable>
        <xsl:attribute name="name">
          <xsl:value-of select="@id" />
        </xsl:attribute>
        <xsl:attribute name="select">
          <xsl:value-of select="text()" />
        </xsl:attribute>
      </metaxsl:variable>
    </xsl:for-each>
  </metaxsl:template>
</metaxsl:template>
  </xsl:template>


  <xsl:template match="/">
    <xsl:call-template name="BuildInstantiateConstantsTemplate_tmpl" />
  </xsl:template>

The idea being I generate the *instantiateConstants_tmpl* dynamically with all my global variable declarations, then call my newly generated template *instantiateConstants_tmpl* to instantiate them in the script. Is something like this possible in XSL?

Frank Rosario
@Frank Rosario: Glad my answer was useful. I f you really liked an answer, you could vote for it.
Dimitre Novatchev
+2  A: 

Hi Frank,

What you want is not possible at present in pure XSLT (1.0 or 2.0).

If you are useing the Saxon 9.x XSLT processor, there is a couple of extension functions that implement this: saxon:compile-stylesheet() and saxon:transform().

It is very rare that a solution to a problem really requires such functionality and it is quite possible that if you describe the problem people will find the best way to solve it without having to produce and execute an XSLT stylesheet dynamically.

Dimitre Novatchev