Pass XML fragments as stylesheet paramters with lxml?
Question
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">
<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
?
Solution
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
OTHER TIPS
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.
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()