Hi Adrian/Stefan,
Apologies for dredging up a 2-year-old thread but I've hit the same issue and 
found that although the posted solution works for variables declared with 
simple string values, it fails when considering xsl:param values or 
"calculated" values.  Consider:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
xmlns:abc="bar" extension-element-prefixes="abc">
    <xsl:output method="xml"/>
    <xsl:param name="param1">default param value</xsl:param>

    <xsl:template match="/">
            <xsl:variable name="var1" select="concat( 'http://', $param1 )"/>
            <abc:foo attr1="{$var1}"/>
            <baz><xsl:value-of select="concat( 'XSLT ouput: ', $var1 )"/></baz>
    </xsl:template>

</xsl:stylesheet> 

Now let's say I want to receive the value of "$var1" in my extension-element 
foo():

#!/usr/local/bin/python3
# -*- coding: utf-8 -*-

import sys

from lxml import etree
from copy import deepcopy

class FooExtensionElement( etree.XSLTExtension ):

    def execute( self, context, self_node, input_node, output_parent ):
        print( "attr1: {}".format( self_node.get( "attr1" )))

parser = etree.XMLParser( load_dtd=True )
extensions = { 
    ( 'bar', 'foo' ) : FooExtensionElement()
}
transformer = etree.XSLT( etree.parse( sys.stdin, parser ), extensions = 
extensions )
xml_doc = etree.XML( '<dummy/>' )
result = transformer( xml_doc, param1=etree.XSLT.strparam( "passed param value" 
))
print( result ) 

When run, the output is:

attr1: {$var1}
<?xml version="1.0"?>
<baz bif="http://passed param value">XSLT ouput: http://passed param value</baz>

This shows that:
* self_node.get( "attr1" ) returns the literal string "{$var1}" in execute(), 
i.e. the transformer has not evaluated {$var1} when passing it to abc:foo/@attr1
* baz/@bif gets the evaluated value of {$var1} as expected

If I were to perform the solution suggested below and lookup //xsl:variable[ 
@name = 'var1']/@select from within execute(), it would return the string 
"concat( 'http://', $param1 )"
I could attempt to evaluate this in self_node.xpath(), but I don't have access 
to the value of $param1, right?
So this would result in: lxml.etree.XPathEvalError: Undefined variable
What if I first looked up the value of $param1 in the same way as suggested 
below for xsl:variable?
//xsl:param[ @name = 'param1']/text() would return "default param value", NOT 
the actual value ("passed param value") which was passed in: AFAIK, I have *no* 
way to access that value from execute().

This problem goes away if evaluation of  {$xyz} -syntax is honored for 
extension-elements.  As demonstrated above, this is already working perfectly 
for elements in the XSL which have no namespace (the "baz" element in my 
example).  Is there a fundamental reason why extension-elements are excluded 
from these evaluations?
If this contravenes XSLT convention, is it possible to place a map of 
variable/param names and their values within the 'context' parameter passed to 
the execute() function so that extension elements can perform their own lookup?

Many thanks!
Mike



Adrian Bool wrote:
> Hi Stefan,
> Many thanks for your thoughts.  I hadn't considered just executing an XPath
> query directly against the XSL as simple XML – but that works!  I combined
> this idea with your xpath expression and now have a function that will
> return the correct value:
> def get_variable_from_xsl(variable_name, xsl):
>     xpath_results = xsl.xpath("//xsl:variable[@name=$name]",
> name=variable_name,
>                     namespaces={'xsl':'http://www.w3.org/1999/XSL/Transform'
> })
>     for xpath_result in xpath_results:
>         if xpath_result.get('name') == variable_name:
>             for xpath_child in xpath_result.getchildren():
>                 if 'select' in xpath_child.keys():
>                     return xpath_child.get('select')
>     return None
> destination_url = get_variable_from_xsl('destination_url', xsl)
> print(f"destination_url = {destination_url}")
> Which returns:
> destination_url = 'https://my.domain.example.com/'
> Thanks again!
> aid
> On Wed, 10 Apr 2019 at 18:25, Stefan Behnel stefan...@behnel.de wrote:
> > Adrian Bool schrieb am 10.04.19 um 10:49:
> > I'm trying to extract from data from XSL files on an IBM DataPower
> > system.
> > I can't change the XSLT files - and therefore need to work around their
> > current structure to get the info I need.
> > Well, XSL is XML, so you can always post-process the XSL document after
> > parsing it as XML, before you pass it into the XSLT() constructor.
> > I'm doing this from within Python 3.7 using etree.__version__ 4.3.3.
> > Here are my imports, a dummy XML object and a cutdown version of the XSL
> > file from the IBM system:
> > from lxml import etree
> > from textwrap import dedent
> > 
> > xml = etree.XML("""<root></root>""")
> > 
> > xsl = etree.XML(dedent(
> >     """<?xml version="1.0"?>
> >     <xsl:stylesheet version="1.0"
> >     xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> >     xmlns:dp="http://www.datapower.com/extensions";
> >     extension-element-prefixes="dp">
> > 
> >     <xsl:template match="node()|@*">
> > <xsl:copy>
> > <xsl:apply-templates select="node()|@*"/>
> > </xsl:copy>
> > 
> >         <xsl:variable name="destination_url">
> >             <xsl:value-of select="'https://my.domain.example.com/'"
> > 
> > />
> >         </xsl:variable>
> > 
> >         <!--Set the destination URL in the context variable-->
> >         <dp:set-variable name="'var://context/mpgw/Destination'"
> > 
> > value="$destination_url" />
> > I don't think I've ever tried this myself in lxml. I guess I would have
> > expected value="{$destination_url}" rather than the plain variable
> > reference here. Can't say how XSLT is meant to deal with this.
> >     </xsl:template>
> >     </xsl:stylesheet>
> >     """))
> > 
> > 
> > My goal is to use lxml to process this file and extract the contents of
> > the
> > DataPower variable 'var://context/mpgw/Destination' - i.e. the contents
> > of
> > the xsl:variable $destination_url - in the above case '
> > https://my.domain.example.com/'.
> > I've created an XSLT extension for the "dp:set-variable" element to try
> > to
> > capture this information:
> > class MyExtElement(etree.XSLTExtension):
> >     def execute(self, context, self_node, input_node, output_parent):
> >         if self_node.tag == '{
> > 
> > http://www.datapower.com/extensions%7Dset-variable':
> >                 print(f"Setting variable of name {self_node.get('name')}
> > to
> > value {self_node.get('value')}")
> > extensions = { ('http://www.datapower.com/extensions',
> > 
> > 'set-variable')
> > : MyExtElement() }
> >     result = etree.XSLT(xsl, extensions=extensions)(xml)
> >     print(f"Result: {result}")
> > When this is being called; instead of the value of destination_url I'm
> > getting the static "$destination_url" string instead - with the output of
> > the above script being:
> > Setting variable of name 'var://context/mpgw/Destination' to value
> > 
> > $destination_url
> >     Result: <?xml version="1.0"?>
> >     <root/>
> > I am rather suprised that the $destination_url xsl:variable was not being
> > evaluated - does anyone know why this is the case?
> > Given the above, I've been trying to find a way to access the
> > $destination_url xsl:variable from within my XSLTExtension - but
> > unfortunately with no luck.
> > Did you try looking it up in the XSL document via XPath? Something like
> > .xpath("//xsl:variable[@name=$name]", name="destination_url")
> > (plus namespaces)
> > Stefan
> > _________________________________________________________________
> > Mailing list for the lxml Python XML toolkit - http://lxml.de/
> > l...@lxml.de
> > https://mailman-mail5.webfaction.com/listinfo/lxml
> >
_______________________________________________
lxml - The Python XML Toolkit mailing list -- lxml@python.org
To unsubscribe send an email to lxml-le...@python.org
https://mail.python.org/mailman3/lists/lxml.python.org/
Member address: arch...@mail-archive.com

Reply via email to