Solution 1:
I came up with a function that changes namespace ns_from
to ns_to
. Removing a namespace can just be realized by setting ns_to
to ""
.
(However, this solution seems to be problematic according the comments by the OP)
def replace_namespace(root, ns_from, ns_to):
"""
Merge the namespace ns_from to ns_to in the tree rooted at the root, everything that belongs to ns_from will be in
ns_to
To do so, I
1. change the tag of root
Change the namespace of root from ns_from to ns_to
2. change the attribute
Change the namespace of root from ns_from to ns_to
3. change the nsmap
delete the ns_from space
4. keep other property
"""
# change the tag of root
tag = etree.QName(root.tag)
# if there are attributes belong to namespace ns_from, update to namespace of the
# attributes to namespace ns_to
if tag.namespace == ns_from:
root.tag = '{%s}%s' % (ns_to, tag.localname)
# change the attribute of root
# if there are attributes belong to namespace ns_from, update to namespace of the
# attributes to namespace ns_to
root_attrib_dict = dict(root.attrib)
new_attrib_dict = {}
for key, value in root_attrib_dict.items():
key_QName = etree.QName(key)
if key_QName.namespace == ns_from:
new_key = '{%s}%s' % (ns_to, key_QName.localname)
new_attrib_dict[new_key] = value
else:
new_attrib_dict[key] = value
# set the new nsmap
new_nsmap = root.nsmap.copy()
for ns_key, ns_value in root.nsmap.items():
if ns_value == ns_from:
del new_nsmap[ns_key]
# make the updated root
new_root = etree.Element(root.tag, attrib=new_attrib_dict, nsmap=new_nsmap)
# copy other properties
new_root.text = root.text
new_root.tail = root.tail
# call recursively
for old_root in root[:]:
new_root.append(replace_namespace(old_root, ns_from, ns_to))
return new_root
Test Codes:
input_xml_string = """
<facturaElectronicaCompraVenta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="facturaElectronicaCompraVenta.xsd">
<header></header>
<detail></detail>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>KvIMPxajMb98G3+HdSLg1/pgSyisLp4OWZt6Gxhe+/c=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>Bv9W9cGyXvX4QeDDb61YME8TbnFlBOVBw2Iiv+a+7VrxjoH4z8kLO4rgonXbqGuk2DKrR4ACqoFQNd/9/lJb31TDk2SjegURBsjP9gLvFWwfq99jh6zn6rPF/gwqd+lA1ruGpDT/Q+vxMXeNpXfk+nDcgdDJoP1bpDEPHbSHGkQu2SX1NQP1SlRZkNoJXxorFfbTDmm1/VFRsv5uBNQvf7hSxTEvvLW8WVYN271iTzHTpAnbyg7VTeys/Ca2FQsZ95hgCHfKsOHEX2/HtxpkGtXDjJKPHq43M2MR3Bp9+YUBAxcj5WMsGcs0lp7hFP6xADEJAcLdfta3SJCdNTa0Vw==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
CertificateStuff...
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</facturaElectronicaCompraVenta>
"""
root = etree.fromstring(input_xml_string)
ns_modify_from = "http://www.w3.org/2000/09/xmldsig#"
ns_modify_to = ""
new_root = replace_namespace(root, ns_modify_from, ns_modify_to)
# create a new elementtree with new_root so that we can use the
# .write method.
tree = etree.ElementTree()
tree._setroot(new_root)
tree.write('done.xml',
pretty_print=True, xml_declaration=True, encoding='UTF-8')
Which gives:
<?xml version='1.0' encoding='UTF-8'?>
<facturaElectronicaCompraVenta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="facturaElectronicaCompraVenta.xsd">
<header/>
<detail/>
<Signature>
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>KvIMPxajMb98G3+HdSLg1/pgSyisLp4OWZt6Gxhe+/c=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Bv9W9cGyXvX4QeDDb61YME8TbnFlBOVBw2Iiv+a+7VrxjoH4z8kLO4rgonXbqGuk2DKrR4ACqoFQNd/9/lJb31TDk2SjegURBsjP9gLvFWwfq99jh6zn6rPF/gwqd+lA1ruGpDT/Q+vxMXeNpXfk+nDcgdDJoP1bpDEPHbSHGkQu2SX1NQP1SlRZkNoJXxorFfbTDmm1/VFRsv5uBNQvf7hSxTEvvLW8WVYN271iTzHTpAnbyg7VTeys/Ca2FQsZ95hgCHfKsOHEX2/HtxpkGtXDjJKPHq43M2MR3Bp9+YUBAxcj5WMsGcs0lp7hFP6xADEJAcLdfta3SJCdNTa0Vw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
CertificateStuff...
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</facturaElectronicaCompraVenta>
Solution 2(Seems to be problematic):
(This is my initial solution, however, I find it problematic, so I modify it and finally get solution 1)
I modify the codes from this answser from the following two aspects:
- I use a recursive function. The original answer only reset
nsmap
for the root node. So there will be a xmlns:ds=***"
left in the Signature
Element(What you meet as you stated in the comments)
- I add codes to update the attribte of the root. If using the original answer, the
xsi:noNamespaceSchemaLocation
attribute will be deleted.
def set_namsespace(root, target_nt):
"""
Only keep the namespace target_nt in the tree rooted at the root, other namespace will be removed,
and the target_nt is set to the default name space.
To do so, I
1. change the tag of root
Change the namespace of root to be target_nt (Sine target_nt is default, it is omitted from the tag)
2. change the attribute
Change the namespace of root to be target_nt
3. change the nsmap
set the target_nt as the default namespace, other namespace will be deleted
4. keep other property
"""
# change the tag of root
tag = etree.QName(root.tag)
if tag.namespace is not None:
root.tag = '{%s}%s' % (target_nt, tag.localname)
# change the attribute of root
# if there are attributes belong to other namespace, update to namespace of the
# attributes to target_nt
root_attrib_dict = dict(root.attrib)
new_attrib_dict = {}
for key, value in root_attrib_dict.items():
key_QName = etree.QName(key)
if key_QName.namespace is not None:
new_key = '{%s}%s' % (target_nt, key_QName.localname)
else:
new_key = key
new_attrib_dict[new_key] = value
# set the new nsmap
# only keep the target_nt, set to default namespace
new_nsmap = {None: target_nt}
# make the updated root
new_root = etree.Element(root.tag, attrib=new_attrib_dict, nsmap=new_nsmap)
# copy other properties
new_root.text = root.text
new_root.tail = root.tail
# call recursively
for old_root in root[:]:
new_root.append(set_namsespace(old_root, target_nt))
return new_root
Test Codes:
root = etree.fromstring(input_xml_string)
target_ns = "http://www.w3.org/2001/XMLSchema-instance"
new_root = set_namsespace(root, target_ns)
# create a new elementtree with new_root so that we can use the
# .write method.
tree = etree.ElementTree()
tree._setroot(new_root)
tree.write('done.xml',
pretty_print=True, xml_declaration=True, encoding='UTF-8')
Which gives:
<?xml version='1.0' encoding='UTF-8'?>
<facturaElectronicaCompraVenta xmlns="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="facturaElectronicaCompraVenta.xsd">
<header/>
<detail/>
<Signature>
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>KvIMPxajMb98G3+HdSLg1/pgSyisLp4OWZt6Gxhe+/c=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Bv9W9cGyXvX4QeDDb61YME8TbnFlBOVBw2Iiv+a+7VrxjoH4z8kLO4rgonXbqGuk2DKrR4ACqoFQNd/9/lJb31TDk2SjegURBsjP9gLvFWwfq99jh6zn6rPF/gwqd+lA1ruGpDT/Q+vxMXeNpXfk+nDcgdDJoP1bpDEPHbSHGkQu2SX1NQP1SlRZkNoJXxorFfbTDmm1/VFRsv5uBNQvf7hSxTEvvLW8WVYN271iTzHTpAnbyg7VTeys/Ca2FQsZ95hgCHfKsOHEX2/HtxpkGtXDjJKPHq43M2MR3Bp9+YUBAxcj5WMsGcs0lp7hFP6xADEJAcLdfta3SJCdNTa0Vw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
CertificateStuff...
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</facturaElectronicaCompraVenta>
This version of codes is kind of problematic. The code set the default name space to what you want to keep, and delete other namespace. This will add an unwanted default namespace declaration xmlns="http://www.w3.org/2001/XMLSchema-instance"
.
Solution 3:
Perhaps I should just reset the namespace?
def set_namsespace(root, target_nt):
"""
Set the target_nt to be default namespace
"""
# set the target_nt to be default namespace
new_nsmap = root.nsmap.copy()
for ns_key, ns_value in root.nsmap.items():
if ns_value == target_nt:
del new_nsmap[ns_key]
new_nsmap[None] = target_nt
# make the updated root
root_attrib_dict = dict(root.attrib)
new_root = etree.Element(root.tag, attrib=root_attrib_dict, nsmap=new_nsmap)
# copy other properties
new_root.text = root.text
new_root.tail = root.tail
# call recursively
for old_root in root[:]:
new_root.append(set_namsespace(old_root, target_nt))
return new_root
Using:
root = etree.fromstring(input_xml_string)
target_ns = "http://www.w3.org/2000/09/xmldsig#"
new_root = set_namsespace(root, target_ns)
Which gives:
<?xml version='1.0' encoding='UTF-8'?>
<facturaElectronicaCompraVenta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="facturaElectronicaCompraVenta.xsd">
<header/>
<detail/>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>KvIMPxajMb98G3+HdSLg1/pgSyisLp4OWZt6Gxhe+/c=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Bv9W9cGyXvX4QeDDb61YME8TbnFlBOVBw2Iiv+a+7VrxjoH4z8kLO4rgonXbqGuk2DKrR4ACqoFQNd/9/lJb31TDk2SjegURBsjP9gLvFWwfq99jh6zn6rPF/gwqd+lA1ruGpDT/Q+vxMXeNpXfk+nDcgdDJoP1bpDEPHbSHGkQu2SX1NQP1SlRZkNoJXxorFfbTDmm1/VFRsv5uBNQvf7hSxTEvvLW8WVYN271iTzHTpAnbyg7VTeys/Ca2FQsZ95hgCHfKsOHEX2/HtxpkGtXDjJKPHq43M2MR3Bp9+YUBAxcj5WMsGcs0lp7hFP6xADEJAcLdfta3SJCdNTa0Vw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
CertificateStuff...
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</facturaElectronicaCompraVenta>