Having all namespace declarations in the root element

Hi,
I made an XML doc grabbing and assembling nodes from various stuffs. namespace declarations are consistent but repeated among various descendant elements. And I want to gather all these namespace declarations in the root element.
i. e. when I have :
<root> <foo xmlns:abc="abc.namespace"> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe xmlns:abc="abc.namespace"> abc:stuff stuff </abc:stuff> </joe> </root>
Then I need (once serialized with .tostring(...) :
<root xmlns:abc="abc.namespace"> <foo> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe> abc:stuff stuff </abc:stuff> </joe> </root>
I naively thought that merging all elements .nsmap attribs into the root.nsmap should do the job, but it appears that changes to the Element.nsmap mapping fail silently.
My code (what I want to do with a really mutable .nsmap) :
----
etree.cleanup_namespaces(doc)
for elem in doc.getiterator():
if hasattr(elem, 'nsmap'):
for k in elem.nsmap:
doc.nsmap[k] = elem.nsmap.pop(k)
----
Unfortunately .nsmap attrib is a so called "non mutable dict but ignoring silently mutation attempts". Thus this code does not work as expected.
Any hint to unlock me is welcome.
Best regards

Am .02.2015, 14:59 Uhr, schrieb Gilles Lenfant gilles.lenfant@gmail.com:
Hi, I made an XML doc grabbing and assembling nodes from various stuffs. namespace declarations are consistent but repeated among various descendant elements. And I want to gather all these namespace declarations in the root element. i. e. when I have :
<root> <foo xmlns:abc="abc.namespace"> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe xmlns:abc="abc.namespace"> <abc:stuff> stuff </abc:stuff> </joe> </root> Then I need (once serialized with .tostring(...) : <root xmlns:abc="abc.namespace"> <foo> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe> <abc:stuff> stuff </abc:stuff> </joe> </root>
I naively thought that merging all elements .nsmap attribs into the root.nsmap should do the job, but it appears that changes to the Element.nsmap mapping fail silently.
I think this happens to us all at some point. What you need to do is use the nsmap argument when creating your root element and pass it in a dictionary of (prefix, namespace).
Charlie

2015-02-12 15:07 GMT+01:00 Charlie Clark charlie.clark@clark-consulting.eu :
Am .02.2015, 14:59 Uhr, schrieb Gilles Lenfant gilles.lenfant@gmail.com:
Hi,
I made an XML doc grabbing and assembling nodes from various stuffs. namespace declarations are consistent but repeated among various descendant elements. And I want to gather all these namespace declarations in the root element. i. e. when I have :
<root> <foo xmlns:abc="abc.namespace"> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe xmlns:abc="abc.namespace"> <abc:stuff> stuff </abc:stuff> </joe> </root> Then I need (once serialized with .tostring(...) : <root xmlns:abc="abc.namespace"> <foo> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe> <abc:stuff> stuff </abc:stuff> </joe> </root>
I naively thought that merging all elements .nsmap attribs into the
root.nsmap should do the job, but it appears that changes to the Element.nsmap mapping fail silently.
I think this happens to us all at some point. What you need to do is use the nsmap argument when creating your root element and pass it in a dictionary of (prefix, namespace).
Charlie
Charlie Clark Managing Director Clark Consulting & Research German Office Kronenstr. 27a Düsseldorf D- 40217 Tel: +49-211-600-3657 Mobile: +49-178-782-6226 _________________________________________________________________
Many thanks Charlie for that fast and efficient hint,
For those who will search for the solution in the ML archive, here it is :
-------- etree.cleanup_namespaces(doc)
global_nsmap = {} for elem in doc.getiterator(): if hasattr(elem, 'nsmap'): global_nsmap.update(elem.nsmap)
new_doc = etree.Element(doc.tag, nsmap=global_nsmap) for elem in doc: new_doc.append(elem) etree.cleanup_namespaces(new_doc) --------

Charlie Clark schrieb am 12.02.2015 um 15:07:
Am .02.2015, 14:59 Uhr, schrieb Gilles Lenfant:
I made an XML doc grabbing and assembling nodes from various stuffs. namespace declarations are consistent but repeated among various descendant elements. And I want to gather all these namespace declarations in the root element. i. e. when I have :
<root> <foo xmlns:abc="abc.namespace"> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe xmlns:abc="abc.namespace"> <abc:stuff> stuff </abc:stuff> </joe> </root> Then I need (once serialized with .tostring(...) : <root xmlns:abc="abc.namespace"> <foo> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe> <abc:stuff> stuff </abc:stuff> </joe> </root>
I naively thought that merging all elements .nsmap attribs into the root.nsmap should do the job, but it appears that changes to the Element.nsmap mapping fail silently.
I think this happens to us all at some point. What you need to do is use the nsmap argument when creating your root element and pass it in a dictionary of (prefix, namespace).
Would it help if the ElementMaker (a.k.a. E-factory) aggregated the namespace declarations of the children it receives? I don't think this is worth changing the way Elements work overall, but changing the ElementMaker sounds doable and reasonable to me.
Stefan

I made an XML doc grabbing and assembling nodes from various stuffs. namespace declarations are consistent but repeated among various
descendant
elements. And I want to gather all these namespace declarations in the
root
element. i. e. when I have :
<root> <foo xmlns:abc="abc.namespace"> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe xmlns:abc="abc.namespace"> <abc:stuff> stuff </abc:stuff> </joe> </root> Then I need (once serialized with .tostring(...) : <root xmlns:abc="abc.namespace"> <foo> <abc:bar attrib="value"> blah </abc:bar> </foo> <joe> <abc:stuff> stuff </abc:stuff> </joe> </root>
[...]
Would it help if the ElementMaker (a.k.a. E-factory) aggregated the namespace declarations of the children it receives? I don't think this is worth changing the way Elements work overall, but changing the
ElementMaker
sounds doable and reasonable to me.
Did the OP actually use the E-factory for tree assembly? Or you probably mean one should then use it to combine nodes to assemble, if aggregation is desired.
Maybe a convenience helper like cleanup_namespaces(...) could be useful? Or a switch to make cleanup_namespaces optionally propagate namespace declarations "upwards" in the tree, if possible (i.e. there's no remapping of the prefix).
Holger
Landesbank Baden-Wuerttemberg Anstalt des oeffentlichen Rechts Hauptsitze: Stuttgart, Karlsruhe, Mannheim, Mainz HRA 12704 Amtsgericht Stuttgart

Holger Joukl schrieb am 13.02.2015 um 10:38:
Would it help if the ElementMaker (a.k.a. E-factory) aggregated the namespace declarations of the children it receives? I don't think this is worth changing the way Elements work overall, but changing the ElementMaker sounds doable and reasonable to me.
Did the OP actually use the E-factory for tree assembly? Or you probably mean one should then use it to combine nodes to assemble, if aggregation is desired.
Yes. Although I guess that most data would already be passed through cleanup_namespaces() anyway when this comes up as an issue, so that might be the 'cleaner' canonical solution.
Maybe a convenience helper like cleanup_namespaces(...) could be useful? Or a switch to make cleanup_namespaces optionally propagate namespace declarations "upwards" in the tree, if possible (i.e. there's no remapping of the prefix).
The latter, I think, maybe as an explicit nsmap that declares the namespaces on the Element that was passed in, iff they are used in the tree.
Patches welcome.
Stefan

Stefan Behnel schrieb am 13.02.2015 um 10:58:
Holger Joukl schrieb am 13.02.2015 um 10:38:
Would it help if the ElementMaker (a.k.a. E-factory) aggregated the namespace declarations of the children it receives? I don't think this is worth changing the way Elements work overall, but changing the ElementMaker sounds doable and reasonable to me.
Did the OP actually use the E-factory for tree assembly? Or you probably mean one should then use it to combine nodes to assemble, if aggregation is desired.
Yes. Although I guess that most data would already be passed through cleanup_namespaces() anyway when this comes up as an issue, so that might be the 'cleaner' canonical solution.
Maybe a convenience helper like cleanup_namespaces(...) could be useful? Or a switch to make cleanup_namespaces optionally propagate namespace declarations "upwards" in the tree, if possible (i.e. there's no remapping of the prefix).
The latter, I think, maybe as an explicit nsmap that declares the namespaces on the Element that was passed in, iff they are used in the tree.
Have fun:
https://github.com/lxml/lxml/commit/4a1c2e9ff3c99247ec1da2707b29e3bb336d641d
Stefan
participants (4)
-
Charlie Clark
-
Gilles Lenfant
-
Holger Joukl
-
Stefan Behnel