0

I would like to generate an xml using an input xml and a xslt file. I have a sample input xml and xslt files here <https://xsltfiddle.liberty-development.net/aiyned/1>. And the final output is what I would like.

Basically the input.xml is:

<?xml version="1.0" encoding="utf-8" ?> <root> <parent1 value="parent1"> <inserthere value="parent1"/> </parent1> <parent2 value="parent2"> <inserthere value="parent2"/> </parent2> </root> 

input.xslt is:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <!-- <xsl:variable name="childDoc" select="document('child.xml')"/> --> <xsl:variable name="childDoc"> <root> <child1 value="child1"/> <child2 value="child2"/> </root> </xsl:variable> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="inserthere"> <xsl:variable name="currentParent" select="."/> <xsl:for-each select="$childDoc/root/node()"> <xsl:copy> <xsl:attribute name="value" select="concat($currentParent/@value,'_',@value)"/> </xsl:copy> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

Output xml is:

<?xml version="1.0" encoding="UTF-8"?><root> <parent1 value="parent1"> <child1 value="parent1_child1"/> <child2 value="parent1_child2"/> </parent1> <parent2 value="parent2"> <child1 value="parent2_child1"/> <child2 value="parent2_child2"/> </parent2> </root> 

The problem is the site is using saxon engine, which I believe could require a license. I would like to generate the output xml using lxml or any python library that is free. Currently when I run

import lxml.etree as ET dom = ET.parse("input.xml") xslt = ET.parse("input.xslt") transform = ET.XSLT(xslt) newdom = transform(dom) print(ET.tostring(newdom, pretty_print=True)) 

I get an error of:

 newdom = transform(dom) File "src/lxml/xslt.pxi", line 602, in lxml.etree.XSLT.__call__ lxml.etree.XSLTApplyError: Failed to evaluate the 'select' expression. 

I think the problem is lxml only support version 1.0? There are some comments here how to use saxon use saxon with python, but I would like to prevent requiring java or other external applications. Is there a way to make the above work with just python? Or is there a way to update my xslt file so that it will work with just lxml transform function?

2
  • Where are the childDoc nodes actually? Are they in a separate XML document, or listed in the XSLT? It makes a difference in XSLT 1.0. Commented Dec 9, 2020 at 21:30
  • Currently the childDoc is just listed in the xslt itself Commented Dec 9, 2020 at 21:33

1 Answer 1

3

Saxon-C 1.2.1 is the latest release of Saxon-C https://www.saxonica.com/saxon-c/index.xml and has a Python API https://www.saxonica.com/saxon-c/doc/html/saxonc.html so you can download https://www.saxonica.com/download/c.xml, install https://www.saxonica.com/saxon-c/documentation/index.html#!starting/installing and run it https://www.saxonica.com/saxon-c/documentation/index.html#!samples/samples_python from Python if you think you need to use XSLT 3.

The HE edition does not require you to buy a license.

As for the error in XSLT, if you want to test XSLT 1.0 code with the xsltfiddle then choose XslCompiledTransform as the XSLT processor and you will get a similar error for your code and the easiest way, if you really declare your variable value inline as a result tree fragment, is to use exsl:node-set:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl" version="1.0"> <xsl:output method="xml"/> <!-- <xsl:variable name="childDoc" select="document('child.xml')"/> --> <xsl:variable name="childDoc"> <root> <child1 value="child1"/> <child2 value="child2"/> </root> </xsl:variable> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="inserthere"> <xsl:variable name="currentParent" select="."/> <xsl:copy-of select="exsl:node-set($childDoc)/root/node()"/> </xsl:template> </xsl:stylesheet> 

https://xsltfiddle.liberty-development.net/aiyned/4

Or if you want to change values then use the template

 <xsl:template match="inserthere"> <xsl:variable name="currentParent" select="."/> <xsl:for-each select="exsl:node-set($childDoc)/root/node()"> <xsl:copy> <xsl:attribute name="value"> <xsl:value-of select="concat($currentParent/@value,'_',@value)"/> </xsl:attribute> </xsl:copy> </xsl:for-each> </xsl:template> 

https://xsltfiddle.liberty-development.net/aiyned/3

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the reply. Yea I rather not install other dependencies, but I appreciate the steps you provided incase I want to install saxon. With your update xsl above, it can be read with lxml correctly like you stated, but the result is no longer the same. Is there a way to have the value updated to like "parentX_childX" etc
See the edit but keep in mind that using <xsl:variable name="childDoc" select="document('child.xml')"/> will save you all the result tree -> node-set conversion trouble your original sample produces.
Thanks a lot, this definitely helps me out!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.