GNOME Bugzilla – Bug 61290
Namespace nodes have no parents.
Last modified: 2009-08-15 18:40:50 UTC
Running the following style sheet against itself: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="*"> <xsl:for-each select="namespace::*"> <namespace> <name><xsl:value-of select="name()"/></name> <uri><xsl:value-of select="."/></uri> <parent><xsl:copy-of select=".."/></parent> </namespace> </xsl:for-each> </xsl:template> </xsl:stylesheet> Gives: <?xml version="1.0"?> <namespace><name>xsl</name><uri>http://www.w3.org/1999/XSL/Transform</uri><parent/></namespace> showing that the namespace node doesn't seem to have a parent!
Right, that's one of the divergences between the XPath model and libxml internal representation that I don't know how to handle cleanly. Namespace nodes are stored as children of the element where they are defined and identified by reference on nodes using them. namespace::* lists all the namespaces in scope but the information of where the nodes where collected (and hence the parent) is not kept. It's an open issue of libxslt implementation ... So far this is the first time anybody discovered it. Did you hit it by running a testsuite or due to a real application ? Daniel
I have hit this bug in a real application: I like to include in an XSLT output tree the pseudo-listing of a XML document (by pseudo listing I mean that since the parser is "eating" information such as entity declarations and references I won't get the real source, but I'd like to get as close as possible). When I match an element in this "source listing mode", I need to generate the declaration of the namespaces that are different than those found on the parent node and was doing so inside a xsl-foreach loop on the namespaces nodes of the current element: <xsl:for-each select="namespace::*"> <xsl:if test="not(parent::*/parent::*/namespace::*[name()=name(current()) and .=current()])"> <xsl:text> xmlns:</xsl:text> <xsl:value-of select="name()"/> <xsl:text>="</xsl:text> <xsl:value-of select="."/> <xsl:text>"</xsl:text> </xsl:if> </xsl:for-each> I don't think the test can be moved in the for-each select attribute without still needing to reference the namespace node's parent since you need a double test to check that a namespace is identical (its string value and its name must be identical). Hope this helps. Eric
Just to add that, in my specific case, there is a workaround using a variable to store the parent nodeset before descending to the namespace nodes: <xsl:variable name="parent" select=".."/> <xsl:for-each select="namespace::*"> <xsl:if test="not($parent/namespace::*[name()=name(current()) and .=current()])"> <xsl:text> xmlns:</xsl:text> <xsl:value-of select="name()"/> <xsl:text>="</xsl:text> <xsl:value-of select="."/> <xsl:text>"</xsl:text> </xsl:if> </xsl:for-each> The bug is thus non blocking, however these differences between XSLT processors are a threat to the portability of style sheets. Thanks, Eric.
Yep, basically that's one of the easiest way to try to avoid the problem while preserving libxml2 binary compatibility, adding the parent(s) information at the nodeset level, unfortunately this break very very easilly if done too simply. I'm still waiting to find a better way to handle this, I may have to allocate an extra structure associated to the node-set hosting the list of parents for the namespace nodes in the set. A real pain. The notion of parent of a namespace node in the XPath model doesn't make that much sense to me, for example what would be the parent for the node representing "xml" ? That part is weak IMHO. thanks a lot for the feedback, I will try to get this fixed. Daniel
Finally fixed that problem. Basically each time a namespace node is added to a node set I actually make a copy of the source node and keep the parent on the 'next' field of that node. It slows dealloaction of nodesets a bit, but at least that last serious conformance problem is now fixed ! paphio:~/XSLT/tests/general -> xsltproc bug-63.xsl ../docs/bug-63.xml <?xml version="1.0"?> <namespace><name>xsl</name><uri>http://www.w3.org/1999/XSL/Transform</uri><parent><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="*"> <xsl:for-each select="namespace::*"> <namespace> <name><xsl:value-of select="name()"/></name> <uri><xsl:value-of select="."/></uri> <parent><xsl:copy-of select=".."/></parent> </namespace> </xsl:for-each> </xsl:template> </xsl:stylesheet></parent></namespace><namespace><name>xml</name><uri>http://www.w3.org/XML/1998/namespace</uri><parent><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="*"> <xsl:for-each select="namespace::*"> <namespace> <name><xsl:value-of select="name()"/></name> <uri><xsl:value-of select="."/></uri> <parent><xsl:copy-of select=".."/></parent> </namespace> </xsl:for-each> </xsl:template> </xsl:stylesheet></parent></namespace> paphio:~/XSLT/tests/general -> thanks for the report ! Daniel
Okay this has been fixed for a couple of released now, thanks, Daniel