J'ai un fichier xml que je dois ouvrir et y apporter des modifications, l'une de ces modifications consiste à supprimer l'espace de nom et le préfixe, puis à les enregistrer dans un autre fichier. Voici le fichier xml:
<?xml version='1.0' encoding='UTF-8'?>
<package xmlns="http://Apple.com/iTunes/importer">
<provider>some data</provider>
<language>en-GB</language>
</package>
Je peux apporter les autres modifications dont j'ai besoin, mais je ne sais pas comment supprimer l'espace de nom et le préfixe. Voici le XML XML dont j'ai besoin:
<?xml version='1.0' encoding='UTF-8'?>
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
Et voici mon script qui va ouvrir et analyser le XML et le sauvegarder:
metadata = '/Users/user1/Desktop/Python/metadata.xml'
from lxml import etree
parser = etree.XMLParser(remove_blank_text=True)
open(metadata)
tree = etree.parse(metadata, parser)
root = tree.getroot()
tree.write('/Users/user1/Desktop/Python/done.xml', pretty_print = True, xml_declaration = True, encoding = 'UTF-8')
Alors, comment pourrais-je ajouter du code dans mon script qui supprimera l’espace de nom et le préfixe?
Remplacer la balise comme le suggère Uku Loskit. En plus de cela, utilisez lxml.objectify.deannotate .
from lxml import etree, objectify
metadata = '/Users/user1/Desktop/Python/metadata.xml'
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(metadata, parser)
root = tree.getroot()
####
for elem in root.getiterator():
if not hasattr(elem.tag, 'find'): continue # (1)
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i+1:]
objectify.deannotate(root, cleanup_namespaces=True)
####
tree.write('/Users/user1/Desktop/Python/done.xml',
pretty_print=True, xml_declaration=True, encoding='UTF-8')
UPDATE
Certaines balises telles que Comment
renvoient une fonction lors de l'accès à l'attribut tag
. ajouté une garde pour ça. (1)
>>> root.tag
'{http://latest/nmc-omc/cmNrm.doc#measCollec}measCollecFile'
>>> etree.QName(root.tag).localname
'measCollecFile'
Addendum: lxml.etree.QName
accepte également les éléments en construction. Donc etree.QName(root.tag).localname
est équivalent à:
etree.QName(root).localname
import xml.etree.ElementTree as ET
def remove_namespace(doc, namespace):
"""Remove namespace in the passed document in place."""
ns = u'{%s}' % namespace
nsl = len(ns)
for elem in doc.getiterator():
if elem.tag.startswith(ns):
elem.tag = elem.tag[nsl:]
metadata = '/Users/user1/Desktop/Python/metadata.xml'
tree = ET.parse(metadata)
root = tree.getroot()
remove_namespace(root, u'http://Apple.com/iTunes/importer')
tree.write('/Users/user1/Desktop/Python/done.xml',
pretty_print=True, xml_declaration=True, encoding='UTF-8')
Utilisé un extrait de code de ici Cette méthode pourrait facilement être étendue pour supprimer tout attribut d’espace de nom en recherchant les balises commençant par "xmlns".
Vous pouvez également utiliser XSLT pour supprimer les espaces de noms ...
XSLT 1.0 (test.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" priority="1">
<xsl:element name="{local-name()}" namespace="">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{local-name()}" namespace="">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Python
from lxml import etree
tree = etree.parse("metadata.xml")
xslt = etree.parse("test.xsl")
new_tree = tree.xslt(xslt)
print(etree.tostring(new_tree, pretty_print=True, xml_declaration=True,
encoding="UTF-8").decode("UTF-8"))
Sortie
<?xml version='1.0' encoding='UTF-8'?>
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
tout ce que vous devez faire c'est:
objectify.deannotate(root, cleanup_namespaces=True)
après avoir récupéré la racine, en utilisant root = tree.getroot()
Voici deux autres moyens de supprimer des espaces de noms. Le premier utilise l'assistant lxml.etree.QName tandis que le second utilise des expressions rationnelles. Les deux fonctions permettent une liste facultative d’espaces de nommage. Si aucune liste d'espaces de noms n'est fournie, tous les espaces de noms sont supprimés. Les clés d'attribut sont également nettoyées.
from lxml import etree
import re
def remove_namespaces_qname(doc, namespaces=None):
for el in doc.getiterator():
# clean tag
q = etree.QName(el.tag)
if q is not None:
if namespaces is not None:
if q.namespace in namespaces:
el.tag = q.localname
else:
el.tag = q.localname
# clean attributes
for a, v in el.items():
q = etree.QName(a)
if q is not None:
if namespaces is not None:
if q.namespace in namespaces:
del el.attrib[a]
el.attrib[q.localname] = v
else:
del el.attrib[a]
el.attrib[q.localname] = v
return doc
def remove_namespace_re(doc, namespaces=None):
if namespaces is not None:
ns = list(map(lambda n: u'{%s}' % n, namespaces))
for el in doc.getiterator():
# clean tag
m = re.match(r'({.+})(.+)', el.tag)
if m is not None:
if namespaces is not None:
if m.group(1) in ns:
el.tag = m.group(2)
else:
el.tag = m.group(2)
# clean attributes
for a, v in el.items():
m = re.match(r'({.+})(.+)', a)
if m is not None:
if namespaces is not None:
if m.group(1) in ns:
del el.attrib[a]
el.attrib[m.group(2)] = v
else:
del el.attrib[a]
el.attrib[m.group(2)] = v
return doc