2010-07-22 26 views
17

Comment puis-je supprimer les espaces et les sauts de ligne dans une chaîne XML dans Python 2.6? J'ai essayé les paquets suivants:Supprimer des espaces dans la chaîne XML

etree: Cet extrait permet de maintenir les espaces blancs d'origine:

xmlStr = '''<root> 
    <head></head> 
    <content></content> 
</root>''' 

xmlElement = xml.etree.ElementTree.XML(xmlStr) 
xmlStr = xml.etree.ElementTree.tostring(xmlElement, 'UTF-8') 
print xmlStr 

Je ne peux pas utiliser Python 2.7 qui fourniraient le paramètre method.

minidom: la même chose:

xmlDocument = xml.dom.minidom.parseString(xmlStr) 
xmlStr = xmlDocument.toprettyxml(indent='', newl='', encoding='UTF-8') 
+0

Cela peut aider à l'aide lxml pour supprimer toutes les lignes vides et-espaces blancs à partir du noeud texte http://stackoverflow.com/a/19396130/973699 – DevC

Répondre

29

La solution est plus facile en utilisant probablement lxml, où vous pouvez définir une option d'analyseur pour ignorer les espaces entre les éléments:

>>> from lxml import etree 
>>> parser = etree.XMLParser(remove_blank_text=True) 
>>> xml_str = '''<root> 
>>>  <head></head> 
>>>  <content></content> 
>>> </root>''' 
>>> elem = etree.XML(xml_str, parser=parser) 
>>> print etree.tostring(elem) 
<root><head/><content/></root> 

Ce sera probablement suffisant pour vos besoins, mais quelques avertissements pour être sur le coffre-fort côté:

Cela vient de supprimer des nœuds blancs entre les éléments, et essayez de ne pas supprimer des nœuds blancs dans les éléments avec un contenu mixte:

>>> elem = etree.XML('<p> spam <a>ham</a> <a>eggs</a></p>', parser=parser) 
>>> print etree.tostring(elem) 
<p> spam <a>ham</a> <a>eggs</a></p> 

Les espaces de début ou de fin provenant de textnodes ne seront pas supprimés. Cependant, dans certaines circonstances, il supprimera encore les nœuds d'espace blanc du contenu mixte: si l'analyseur n'a pas encore rencontré de nœuds non-blancs à ce niveau.

>>> elem = etree.XML('<p><a> ham</a> <a>eggs</a></p>', parser=parser) 
>>> print etree.tostring(elem) 
<p><a> ham</a><a>eggs</a></p> 

Si vous ne voulez pas, vous pouvez utiliser xml:space="preserve", qui sera respectée. Une autre option utiliserait un dtd et utiliserait etree.XMLParser(load_dtd=True), où l'analyseur utiliserait le dtd pour déterminer quels nœuds d'espace sont significatifs ou non.

Autre que cela, vous devrez écrire votre propre code pour supprimer l'espace que vous ne voulez pas (descendants itérer, et où les propriétés appropriées, définissez .text et .tail qui ne contiennent que des espaces à None ou chaîne vide)

+0

J'ai trouvé que, comme le souligne @Steven, certains éléments ne contenant que des espaces blancs ne sont pas nettoyés. J'ai utilisé une regex après l'appel de 'etree.tostring':' re.sub (r '> \ s + <', '><', xml_str) ' – Rodrigue

+0

Veuillez remplacer' etree.XML (xml_str, parser = p) ' avec 'etree.XML (xml_str, parser = parser)' dans le premier extrait. –

-3
xmlStr = ' '.join(xmlStr.split())) 

Cela met tout le texte dans une ligne remplaçant multiple espace blanc avec un seul blanc.

xmlStr = ''.join(xmlStr.split())) 

Cela éliminerait complètement l'espace, y compris les espaces à l'intérieur du texte et ne peut pas être utilisé.

La première forme pourrait être utilisé avec le risque (mais que vous demandez), pour l'entrée que vous avez donné:

xmlStr = '''<root> 
    <head></head> 
    <content></content> 
</root>''' 
xmlStr = ' '.join(xmlStr.split()) 
print xmlStr 
""" Output: 
<root> <head></head> <content></content> </root> 
""" 

Ce serait valide xml. Il faudrait peut-être vérifier avec une sorte de vérificateur de xml peut-être. Êtes-vous sûr que vous voulez du XML? Avez-vous lu l'article: Python Is Not Java

+0

-1 Votre suggestion va jeter quelque chose comme '" "" \ t \ tfoo "" "' –

+0

Je vais devoir être d'accord avec John. Cela ne préserve pas la syntaxe XML du tout. – mattbasta

6

Les espaces sont importants dans un document XML. L'utilisation d'espaces pour l'indentation est une mauvaise utilisation de XML, car elle introduit des données significatives là où il n'y en a pas vraiment - et malheureusement, c'est la norme. Toute approche programmatique que vous adopterez pour supprimer les espaces sera, au mieux, une supposition - vous devez avoir une meilleure connaissance de ce que le XML transmet pour supprimer correctement les espaces, sans passer par des éléments de données.

-1

Une solution peu maladroite sans lxml :-)

data = """<root> 

    <head></head> <content></content> 

</root>""" 

data3 = [] 
data2 = data.split('\n') 
for x in data2: 
    y = x.strip() 
    if y: data3.append(y) 
data4 = ''.join(data3) 
data5 = data4.replace(" ","").replace("> <","><") 

print data5 

Output: <root><head></head><content></content></root> 
-1

Si les espaces dans les noeuds « non-feuille » est ce que nous essayons de supprimer ensuite la fonction suivante fera (récursive si spécifié):

from xml.dom import Node 

def stripNode(node, recurse=False): 
    nodesToRemove = [] 
    nodeToBeStripped = False 

    for childNode in node.childNodes: 
     # list empty text nodes (to remove if any should be) 
     if (childNode.nodeType == Node.TEXT_NODE and childNode.nodeValue.strip() == ""): 
      nodesToRemove.append(childNode) 

     # only remove empty text nodes if not a leaf node (i.e. a child element exists) 
     if childNode.nodeType == Node.ELEMENT_NODE: 
      nodeToBeStripped = True 

    # remove flagged text nodes 
    if nodeToBeStripped: 
     for childNode in nodesToRemove: 
      node.removeChild(childNode) 

    # recurse if specified 
    if recurse: 
     for childNode in node.childNodes: 
      stripNode(childNode, True) 

Cependant, Thanatos est correct. Les espaces peuvent représenter des données en XML, à utiliser avec précaution.

17

Voici quelque chose que je suis rapide avec parce que je ne voulais pas utiliser lxml:

from xml.dom import minidom 
from xml.dom.minidom import Node 

def remove_blanks(node): 
    for x in node.childNodes: 
     if x.nodeType == Node.TEXT_NODE: 
      if x.nodeValue: 
       x.nodeValue = x.nodeValue.strip() 
     elif x.nodeType == Node.ELEMENT_NODE: 
      remove_blanks(x) 

xml = minidom.parse('file.xml') 
remove_blanks(xml) 
xml.normalize() 
with file('file.xml', 'w') as result: 
    result.write(xml.toprettyxml(indent = ' ')) 

que j'ai vraiment besoin que de réindenter le fichier XML avec indentation autrement cassé. Il ne respecte pas la directive preserve, mais, honnêtement, il en est de même de tant d'autres logiciels traitant des XML, que c'est plutôt une exigence amusante :) En outre, vous pourriez facilement ajouter ce genre de fonctionnalité au code ci-dessus (il suffit de cocher pour space attribut, et ne recure si sa valeur est « préserver ».)

+1

Merci pour cela - je ne voulais pas ajouter lxml à mon projet et cela a parfaitement fonctionné pour mes besoins. –

+2

Génial. Merci mec. –

0
xmlStr = xmlDocument.toprettyxml(indent='\t', newl='\n', encoding='UTF-8') 
fix = re.compile(r'((?<=>)(\n[\t]*)(?=[^<\t]))|(?<=[^>\t])(\n[\t]*)(?=<)') 
newXmlStr = re.sub(fix, '', xmlStr) 

de this source

0

La seule chose qui me dérange le toprettyxml de xml.dom.minidom() est que il ajoute des lignes vides. Je ne semble pas obtenir les composants séparés, donc je viens d'écrire une fonction simple pour supprimer les lignes vides:

#!/usr/bin/env python 

import xml.dom.minidom 

# toprettyxml() without the blank lines 
def prettyPrint(x): 
    for line in x.toprettyxml().split('\n'): 
     if not line.strip() == '': 
      print line 

xml_string = "<monty>\n<example>something</example>\n<python>parrot</python>\n</monty>" 

# parse XML 
x = xml.dom.minidom.parseString(xml_string) 

# clean 
prettyPrint(x) 

Voici ce que les sorties de code:

<?xml version="1.0" ?> 
<monty> 
     <example>something</example> 
     <python>parrot</python> 
</monty> 

Si je toprettyxml() par lui-même, à savoir impression (toprettyxml (x)), il ajoute des lignes vides inutiles:

<?xml version="1.0" ?> 
<monty> 


     <example>something</example> 


     <python>parrot</python> 


</monty>