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 -- Gilles Lenfant
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
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