views:

340

answers:

3

I'm starting to use lxml in Python for processing XML/XSL documents, and in general it seems very straight forward. However, I'm not able to find a way to pass an XML fragment as a stylesheet parameter when doing a translation.

For example, in PHP it is possible to pass DOMDocument XML fragments as stylesheet parameters, so that one can have complex params available within the stylesheet:

$xml = new DOMDocument();
$xml->loadXML('<root><node/></root>');

$xsl = new DOMDocument();
$xsl->loadXML('<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

<xsl:output method="html" omit-xml-declaration="yes" 
    indent="yes" media-type="text/html" />
<xsl:param name="a" />

<xsl:template match="/">
<html>
  <body>
    <xsl:value-of select="$a/foo/bar/text()" />
  </body>
</html>
</xsl:template>

</xsl:stylesheet>');

$fragment = new DOMDocument();
$fragment->loadXML('<foo><bar>baz</bar></foo>');

$proc = new XSLTProcessor;
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsl);

$param_ns = '';
$param_name = 'a';
$proc->setParameter($param_ns, $param_name, $fragment->documentElement);

Which will result in:

<html>
  <body>
  baz
  </body>
</html>

How does one accomplish this using lxml?

+1  A: 

As far as I know, you can only use xpath expressions (or result of the etree.XSLT.strparam() method for strings with quotes) in lxml (at the moment anyway).

However, because you can use xpath expressions, that means you could "work around" by using a custom xpath extension function that returns the Element in question. Another way could be to make use of the standard xpath document() function, and use a custom resolver (sorry, no link to the docs here because of one hyperlink limit).

Steven
Thanks. Could you edit your post and change the link to the page with the custom resolver? I noticed the custom xpath functions after posting and came to the same conclusion, but the `document()` method may be more useful.
digitala
A: 

I believe what you're looking for can be performed using lxml.etree.XSLT. Good examples can be found in the XSLT section on the page documenting XPath and XSLT with lxml.

jathanism
A: 

hi,

You can use a python extension function to return an etree.XML document to xslt. Here's the bare minimum example. It registers a global namespace (uri:params), associations a function with the "params" method in that namespace. That's used by the stylesheet to reach the extension function. Don't forget the xmlns:ptest declaration in the stylesheet.

hmm, looks like I'm going to have to put in html entities to represent < symbol in this code. I have also put it in http://bkc.pastebin.com/f67461ccc


from lxml import etree

stylesheet = etree.XML("""
    <xsl:stylesheet version="1.0"
         xmlns:ptest="uri:params"
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
         <xsl:variable name="params" select="ptest:params()" />
         <xsl:template match="/">
             <name_is><xsl:value-of select="$params/name" /></name_is>
         </xsl:template>
     </xsl:stylesheet>
""")

def params(context):
    # this is the extension function, it returns
    # a etree.XML document
    return etree.XML(
        """<params>
                <name>fred
        </params>"""
        )


def test():
    """test passing document to xslt via python extension function

    >>> test()
    <?xml version="1.0"?>
    <name_is xmlns:ptest="uri:params">fred
    <BLANKLINE>
    """    
    ns = etree.FunctionNamespace('uri:params') # register global namespace
    ns['params'] = params # define function in new global namespace
    transform = etree.XSLT(stylesheet)
    print str(transform(etree.XML("""<source />""")))

if __name__ == "__main__":
    import doctest
    doctest.testmod()
bkc