2009-03-20 11 views
5

Ich muss Leerzeichen zwischen XML-Tags, z. wenn die ursprünglichen xml wie folgt aussehen:Crunching XML mit Python

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

würde Ich mag das Endergebnis seiner bis auf einzelne Zeile knirschte:

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

Bitte beachten Sie, dass ich nicht die Kontrolle über die xml habe Struktur, so sollte die Lösung generisch genug sein, um mit jedem gültigen XML umgehen zu können. Auch das XML könnte CDATA-Blöcke enthalten, die ich von diesem Crunching ausschließen und sie wie es ist lassen sollte.

Ich habe bis jetzt paar Ideen: (1) Parsen Sie das XML als Text und suchen Sie nach Anfang und Ende der Tags < und> (2) ein weiterer Ansatz ist das XML-Dokument zu laden und gehen Knoten für Knoten und Drucken Sie ein neues Dokument durch Verketten der Tags.

Ich denke, beide Methoden würden funktionieren, aber ich möchte das Rad hier nicht neu erfinden, also gibt es vielleicht eine Python-Bibliothek, die schon so etwas macht? Wenn nicht, dann irgendwelche Probleme/Fallstricke zu beachten, wenn Sie meine eigenen Cruncher? Irgendwelche Empfehlungen?

EDIT Vielen Dank für Antworten/Vorschläge, sowohl Triptychon der und Van Gale Lösungen für mich arbeiten und tun genau das, was ich will. Ich wünschte, ich könnte beide Antworten akzeptieren.

Antwort

4

Ziemlich einfach mit BeautifulSoup.

Diese Lösung geht davon aus, dass es ok ist, Whitespace von den Schwanzenden der Zeichendaten zu entfernen.
Beispiel: <foo> bar </foo> wird <foo>bar</foo>

Es ignoriert Kommentare und CDATA korrekt.

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

Ich glaube nicht, dass dies völlig richtig ist, weil es gültige Leerzeichen am Ende des Inhalts entfernt. Aber es hat mich daran erinnert, dass mein Schnipsel mit CDATA das Falsche macht, also danke dafür! :) –

+0

Danke! Das macht genau das, was ich wollte –

+0

Aber das ändert das Dokument! Es ist kein gleiches XML-Dokument mehr ... –

5

würde ich XSLT verwenden:

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

, die den Trick tun sollten.

In Python könnten Sie lxml (direct link to sample on homepage) verwenden, um es zu transformieren.

Für einige Tests verwenden xsltproc, Beispiel:

xsltproc test.xsl test.xml 

wo test.xsl ist die Datei, die oben und test.xml Ihre XML-Datei.

+0

Ich weiß nichts über XSLT aber wenn das die Arbeit erledigt, es sieht wirklich cool ;-) –

+0

XSLT ist wirklich toll, wenn XML-XML vorzugsweise zu transformieren. Es ist in der Tat eine turing-vollständige funktionale Programmiersprache, aber normale Programmierung ist (zumindest in XSLT1.x) ein bisschen schmerzhaft, da Funktionsaufruf-Typen sehr lang sind ;-) –

+0

Danke, ich werde es versuchen, von den ersten Seitenblicken wie es sollte den Trick tun –

2

Nicht wirklich eine Lösung, aber da Sie nach Empfehlungen gefragt haben: Ich würde von einer eigenen Analyse abraten (es sei denn, Sie wollen lernen, einen komplexen Parser zu schreiben), weil nicht alle Leerzeichen entfernt werden sollten. Es gibt nicht nur CDATA-Blöcke, sondern auch Elemente mit dem Attribut "xml: space = preserve", die in XHTML Dingen wie <pre> entsprechen (wobei die eingeschlossenen Whitespaces tatsächlich Bedeutung haben) und einen Parser schreiben, der diese Elemente erkennen kann und Lassen Sie den Whitespace allein wäre möglich, aber unangenehm.

Ich würde mit der Analysemethode gehen, d. H. Das Dokument laden und Knoten für Knoten drucken sie aus. Auf diese Weise können Sie leicht identifizieren, auf welchen Knoten Sie die Leerzeichen entfernen können und welche nicht. Es gibt einige Module in der Python-Standardbibliothek, von denen ich noch keine verwendet habe ;-), die dir vielleicht nützlich sein könnten ... versuche xml.dom, oder ich bin mir nicht sicher, ob du das mit xml.parsers.expat machen kannst.

8

Das ist ziemlich leicht mit lxml behandelt (Anmerkung: diese Besonderheit nicht in ElementTree ist):

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) 

Ergebnisse in:

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

Edit: Die Antwort von Triptychon erinnert Ich über die CDATA-Anforderungen, so sollte die Zeile, die das Parser-Objekt erzeugt, tatsächlich so aussehen:

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

Wenn CDATA vorhanden ist, dann würde diese Methode html alles im CDATA-Block kodieren, z

+0

Funktioniert jetzt mit den Änderungen an der Zeile, die den Parser erstellt. –