2009-03-20 24 views
5

Necesito eliminar espacios en blanco entre etiquetas xml, p. Ej. si el XML original se parece a:Crunching xml with python

<node1> 
    <node2> 
     <node3>foo</node3> 
    </node2> 
</node1> 

Me gustaría que el resultado final para ser ha procesado a una sola línea:

<node1><node2><node3>foo</node3></node2></node1> 

Tenga en cuenta que no voy a tener el control sobre el xml estructura, por lo que la solución debe ser lo suficientemente genérica como para poder manejar cualquier xml válido. Además, el xml podría contener bloques CDATA, que yo necesitaría excluir de este al procesar y dejarlos como están.

Tengo un par de ideas hasta ahora: (1) analizar el xml como texto y buscar el inicio y el final de las etiquetas < y> (2) otro enfoque es cargar el documento xml e ir nodo por nodo y imprima un documento nuevo al concatenar las etiquetas.

Creo que cualquiera de los dos métodos funcionaría, pero prefiero no reinventar la rueda aquí, entonces ¿puede que haya una biblioteca de Python que ya haga algo como esto? Si no, ¿hay algún problema que deba tener en cuenta al implementar mi propio cruncher? ¿Alguna recomendación?

EDITAR Gracias a todos por respuestas/sugerencias, tanto Tríptico de soluciones y de Van Gale funciona para mí y hacer exactamente lo que quiero. Ojalá pudiera aceptar ambas respuestas.

Respuesta

4

Bastante sencillo con BeautifulSoup.

Esta solución asume que está bien quitar el espacio en blanco de los extremos de los datos de caracteres.
Ejemplo: <foo> bar </foo> se convierte en <foo>bar</foo>

Ignorará correctamente los comentarios y CDATA.

import BeautifulSoup 

s = """ 
<node1> 
    <node2> 
     <node3>foo</node3> 
    </node2> 
    <node3> 
     <!-- I'm a comment! Leave me be! --> 
    </node3> 
    <node4> 
    <![CDATA[ 
     I'm CDATA! Changing me would be bad! 
    ]]> 
    </node4> 
</node1> 
""" 

soup = BeautifulSoup.BeautifulStoneSoup(s) 

for t in soup.findAll(text=True): 
    if type(t) is BeautifulSoup.NavigableString: # Ignores comments and CDATA 
     t.replaceWith(t.strip()) 

print soup 
+0

No creo que esto sea correcto porque quitará espacios en blanco válidos al final de los contenidos. Pero, me recordó que mi fragmento hace lo incorrecto con CDATA, ¡así que gracias por eso! :) –

+0

¡Gracias! Esto hace exactamente lo que yo quería –

+0

¡Pero eso CAMBIA el documento! Ya no es un documento XML igual ... –

5

que haría uso de XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="*"> 
     <xsl:copy> 
      <xsl:copy-of select="@*" /> 
      <xsl:apply-templates /> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Que debe hacer el truco.

En python puede usar lxml (direct link to sample on homepage) para transformarlo.

Para algunas pruebas, utilice xsltproc, la muestra:

xsltproc test.xsl test.xml 

donde test.xsl es el archivo de arriba y test.xml su archivo XML.

+0

No sé nada de XSLT pero si que hace el trabajo, se ve realmente genial ;-) –

+0

XSLT es realmente grande al transformar XML preferentemente a XML. De hecho, es un lenguaje de programación funcional completo, pero la programación normal es (al menos en XSLT1.x) un poco dolorosa, ya que los tipos de invocación de funciones son muy largos ;-) –

+0

Gracias, lo intentaré, desde las primeras costuras de aspecto como debería hacer el truco –

2

No es realmente una solución, pero ya que pidió recomendaciones: le aconsejo que no haga su propio análisis (a menos que desee aprender a escribir un analizador complejo) porque, como usted dice, no se deben eliminar todos los espacios. No solo hay bloques CDATA sino también elementos con el atributo "xml: espacio = preservar", que corresponden a cosas como <pre> en XHTML (donde los espacios en blanco encerrados realmente tienen significado) y escribir un analizador que es capaz de reconocer esos elementos y dejar el espacio en blanco solo sería posible pero desagradable.

Me gustaría ir con el método de análisis sintáctico, es decir, cargar el documento e ir nodo por nodo imprimiéndolos. De esta forma, puede identificar fácilmente los nodos con los que puede quitar los espacios y los que no. Hay algunos módulos en la biblioteca estándar de Python, ninguno de los cuales he usado alguna vez ;-) que podrían serle útiles ... intente xml.dom, o no estoy seguro si podría hacerlo con xml.parsers.expat.

8

Esto es bastante fácil de manejar con lxml (nota: esta característica particular no está en elementtree):

from lxml import etree 

parser = etree.XMLParser(remove_blank_text=True) 

foo = """<node1> 
    <node2> 
     <node3>foo </node3> 
    </node2> 
</node1>""" 

bar = etree.XML(foo, parser) 
print etree.tostring(bar,pretty_print=False,with_tail=True) 

Resultados en:

<node1><node2><node3>foo </node3></node2></node1> 

Editar: La respuesta por tríptico recuerda acerca de los requisitos de CDATA, por lo que la línea que crea el objeto del analizador debería verse así:

parser = etree.XMLParser(remove_blank_text=True, strip_cdata=False) 
+0

Si CDATA está presente, este método codificará html todo dentro del bloque CDATA, por ejemplo convirtiendo

+0

Funciona ahora con los cambios en la línea que crea el analizador. –