2009-03-20 17 views
5

Potrzebuję usunąć białe spacje między tagami xml, np. jeśli oryginalny xml wygląda następująco:Crunching xml z pythonem

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

Chciałbym końcowy rezultat będzie nawał dół do jednej linii:

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

Należy pamiętać, że nie będę mieć kontrolę nad xml strukturę, więc rozwiązanie powinno być na tyle ogólne, aby móc obsłużyć dowolny poprawny xml. Również xml może zawierać bloki CDATA, które muszę wykluczyć z tego chrupania i pozostawić je tak jak jest.

Mam kilka pomysłów do tej pory: (1) parsować xml jako tekst i szukać początku i końca tagów < i> (2) innym podejściem jest załadowanie dokumentu xml i przejście od węzła po węźle i wydrukuj nowy dokument nowy, łącząc znaczniki.

Myślę, że albo metoda by działała, ale wolałbym nie wymyślać koła tutaj, więc może być tam biblioteka Pythona, która już robi coś takiego? Jeśli nie, to czy są jakieś problemy/pułapki, o których należy pamiętać po wprowadzeniu własnego klienta cruncher? Wszelkie zalecenia?

EDIT Dziękuję wszystkim za odbieranie/sugestie, zarówno Tryptyk i rozwiązania Van Gale pracy dla mnie i robić dokładnie to, co chcę. Chciałbym móc przyjąć obie odpowiedzi.

Odpowiedz

4

Całkiem proste z BeautifulSoup.

To rozwiązanie zakłada, że ​​można pozbyć się białych znaków z końcowych końców danych znaków.
Przykład: <foo> bar </foo> staje się <foo>bar</foo>

Będzie poprawnie ignorować komentarze i 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

Nie sądzę, że to całkiem słuszne, ponieważ usunie ważne białe spacje na końcu zawartości. Ale przypomniało mi się, że mój fragment robi coś złego z CDATA, więc dzięki za to! :) –

+0

Dzięki! Dokładnie to chciałem –

+0

Ale to ZMIEŃ dokument! To już nie jest równy dokument XML ... –

5

użyję 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> 

To powinno załatwić sprawę.

W języku Python można użyć lxml (direct link to sample on homepage), aby go przekształcić.

Dla niektórych testach, należy xsltproc, próbka:

xsltproc test.xsl test.xml 

gdzie test.xsl jest plik powyżej test.xml plik XML.

+0

wiem nic o XSLT, ale jeśli to nie praca, to wygląda naprawdę cool ;-) –

+0

XSLT jest naprawdę wielki, kiedy przekształcanie XML korzystnie XML. Jest to rzeczywiście kompletny funkcjonalny język programowania, ale normalne programowanie to (przynajmniej w XSLT1.x) odrobina bólu, ponieważ funkcje wywoływania funkcji są bardzo długie ;-) –

+0

Dzięki, spróbuję, od pierwszego spojrzenia szwów tak jak powinno wystarczyć –

2

To nie jest rozwiązanie, ale ponieważ pytasz o zalecenia: Odradzam robienie własnych analiz (chyba że chcesz nauczyć się pisać złożony parser), ponieważ, jak mówisz, nie wszystkie przestrzenie powinny zostać usunięte. Istnieją nie tylko bloki CDATA, ale także elementy z atrybutem "xml: space = preserve", które odpowiadają obiektom takim, jak <pre> w XHTML (gdzie zawarte w rzeczywistości spacje mają znaczenie) i pisaniu analizatora składni, który jest w stanie rozpoznać te elementy i zostawić białą przestrzeń w spokoju byłoby możliwe, ale nieprzyjemne.

Chciałbym iść z metodą parsowania, tj. Załadować dokument i przejść po kolei węzeł je wydrukować. W ten sposób możesz łatwo zidentyfikować, z których węzłów możesz usunąć przestrzenie, a które nie. W bibliotece standardowej Pythona znajduje się kilka modułów, z których żaden nigdy wcześniej nie używał ;-), które mogą ci się przydać ... spróbuj xml.dom, lub nie jestem pewien, czy mógłbyś to zrobić z xml.parsers.expat.

8

to dość łatwo obchodzić lxml (uwaga: ta szczególna cecha nie jest w 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) 

Wyniki w:

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

Edit: Odpowiedź przez Tryptyku przypomnienia mnie o wymaganiach CDATA, więc linia tworząca obiekt analizatora składni powinna wyglądać tak:

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

Jeśli CDATA jest obecna, to ta metoda kodowałaby wszystko wewnątrz bloku CDATA, np. konwersja

+0

Działa teraz ze zmianami w linii tworząc parser. –