2011-10-26 15 views
18

Mi interessa equivalenza dei due elementi XML; e ho scoperto che testare il tostring degli elementi funziona; tuttavia, sembra hacky. C'è un modo migliore per testare l'equivalenza di due elementi etree? Esempio:test di equivalenza di xml.etree.ElementTree

import xml.etree.ElementTree as etree 
h1 = etree.Element('hat',{'color':'red'}) 
h2 = etree.Element('hat',{'color':'red'}) 

h1 == h2 

False

etree.tostring(h1) == etree.tostring(h2) 

Vero

+0

Una funzione per confrontare due elementi possono essere trovati in [risposta di Itamar] (http://stackoverflow.com/a/24349916/2371522) qui sotto . – One

risposta

0

fare targa non l'oro. Quello che hai è un buon confronto. Alla fine XML è TEXT.

+0

Sì, e se si è preoccupati per la formattazione, convertire in ET, quindi eseguire il dump della stringa e confrontare. – Wyrmwood

2

Un modo usuale per confrontare strutture complesse è quello di metterli in una rappresentazione testuale unico comune e confrontare le stringhe risultanti per l'uguaglianza.

per confrontare due stringhe ricevuti JSON, li dovrebbe convertire in oggetti JSON, e poi convertire di nuovo a corde (con lo stesso convertitore) e confrontare. L'ho fatto per controllare i feed JSON, funziona bene.

Per XML, è quasi lo stesso, ma potrebbe essere necessario gestire (rimuovere con strip?) Le parti ".text" (il testo, vuoto o meno, che può essere trovato all'esterno dei tag).

Così, in breve, la soluzione non è un hack, a patto che vi assicurate due XMLs equivalenti (secondo la vostra contesto) avrà la stessa rappresentazione di stringa.

3

Credeteci o no che è in realtà il modo migliore per gestire il confronto di due nodi se non si conoscono quanti bambini possono avere ciascuno e si desidera includere tutti i bambini nella ricerca.

Naturalmente, se avete semplicemente un nodo senza figli, come quello che si sta dimostrando, si può semplicemente confrontare il tag, attributi, e le proprietà di coda:

if h1.tag == h2.tag and h1.attrib == h2.attrib and h1.tail == h2.tail: 
    print("h1 and h2 are the same") 
else 
    print("h1 and h2 are the different") 

non vedo alcun vantaggio principale di questo sopra usando il tostring, comunque.

+0

Puoi anche inserire il testo in base alle tue esigenze: 'h1.text == h2.text' – bmaupin

+0

Questo non confronta gli elementi figli ... – drevicko

7

Confronto tra stringhe non sempre funziona. L'ordine degli attributi non dovrebbe avere importanza considerando due nodi equivalenti. Tuttavia, se esegui il confronto delle stringhe, l'ordine ovviamente conta.

non sono sicuro se si tratta di un problema o di una funzione, ma la mia versione della lxml.etree preserva l'ordine degli attributi se vengono analizzati da un file o una stringa:

>>> from lxml import etree 
>>> h1 = etree.XML('<hat color="blue" price="39.90"/>') 
>>> h2 = etree.XML('<hat price="39.90" color="blue"/>') 
>>> etree.tostring(h1) == etree.tostring(h2) 
False 

Questo potrebbe essere dipendente dalla versione (io uso Python 2.7.3 con lxml.etree 2.3.2 su Ubuntu); Ricordo che non riuscivo a trovare un modo di controllare l'ordine degli attributi di un anno fa o giù di lì, quando ho voluto (per motivi di leggibilità).

Come ho bisogno di confrontare i file XML che sono stati prodotti da diversi serializzatori, non vedo altro modo di confrontare ricorsivamente tag, testo, attributi e figli di ogni nodo. E ovviamente coda, se c'è qualcosa di interessante lì.

Confronto di lxml e xml.etree.ElementTree

La verità è che può essere a carico di attuazione. Apparentemente, lxml usa un dt ordinato o qualcosa del genere, lo standard xml.etree.ElementTree non conserva l'ordine degli attributi: (. Sì, i ritorni a capo mancano Ma è un problema minore.)

Python 2.7.1 (r271:86832, Nov 27 2010, 17:19:03) [MSC v.1500 64 bit (AMD64)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from lxml import etree 
>>> h1 = etree.XML('<hat color="blue" price="39.90"/>') 
>>> h2 = etree.XML('<hat price="39.90" color="blue"/>') 
>>> etree.tostring(h1) == etree.tostring(h2) 
False 
>>> etree.tostring(h1) 
'<hat color="blue" price="39.90"/>' 
>>> etree.tostring(h2) 
'<hat price="39.90" color="blue"/>' 
>>> etree.dump(h1) 
<hat color="blue" price="39.90"/>>>> etree.dump(h2) 
<hat price="39.90" color="blue"/>>>> 

>>> import xml.etree.ElementTree as ET 
>>> h1 = ET.XML('<hat color="blue" price="39.90"/>') 
>>> h1 
<Element 'hat' at 0x2858978> 
>>> h2 = ET.XML('<hat price="39.90" color="blue"/>') 
>>> ET.dump(h1) 
<hat color="blue" price="39.90" /> 
>>> ET.dump(h2) 
<hat color="blue" price="39.90" /> 
>>> ET.tostring(h1) == ET.tostring(h2) 
True 
>>> ET.dump(h1) == ET.dump(h2) 
<hat color="blue" price="39.90" /> 
<hat color="blue" price="39.90" /> 
True 

Un'altra domanda potrebbe essere quello che è considerato whan poco importante il confronto. Ad esempio, alcuni frammenti potrebbero contenere spazi aggiuntivi e non vogliamo preoccuparci. In questo modo, è sempre meglio scrivere alcune funzioni di serializzazione che funzionano esattamente di cui abbiamo bisogno.

+1

' .dump (...) 'restituisce' None', quindi ' ET.dump (h1) == ET.dump (h2) 'sta effettivamente confrontando' Nessuna' con 'Nessuna'. –

4

La serializzazione e la deserializzazione non funzioneranno per XML perché gli attributi non dipendono dall'ordine (e altri motivi) E.g. questi due elementi sono logicamente gli stessi, ma diverse stringhe:

<THING a="foo" b="bar"></THING> 
<THING b="bar" a="foo" /> 

esattamente come fare un confronto elemento è difficile. Per quanto posso dire, non c'è nulla di costruito nell'albero degli elementi per farlo per te. Avevo bisogno di farlo da solo, e ho usato il codice qui sotto. Funziona per i miei bisogni, ma non è adatto a grandi strutture XML e non è veloce o efficiente! Questa è una funzione di ordinamento piuttosto che una funzione di uguaglianza, quindi un risultato di 0 è uguale e qualsiasi altra cosa non lo è. Avvolgerlo con una funzione di ritorno Vero o Falso è lasciato come esercizio per il lettore!

def cmp_el(a,b): 
    if a.tag < b.tag: 
     return -1 
    elif a.tag > b.tag: 
     return 1 
    elif a.tail < b.tail: 
     return -1 
    elif a.tail > b.tail: 
     return 1 

    #compare attributes 
    aitems = a.attrib.items() 
    aitems.sort() 
    bitems = b.attrib.items() 
    bitems.sort() 
    if aitems < bitems: 
     return -1 
    elif aitems > bitems: 
     return 1 

    #compare child nodes 
    achildren = list(a) 
    achildren.sort(cmp=cmp_el) 
    bchildren = list(b) 
    bchildren.sort(cmp=cmp_el) 

    for achild, bchild in zip(achildren, bchildren): 
     cmpval = cmp_el(achild, bchild) 
     if cmpval < 0: 
      return -1 
     elif cmpval > 0: 
      return 1  

    #must be equal 
    return 0 
+0

La causa principale del problema nel confronto tra due file XML è la formattazione diversa, come ha detto in precedenza. E, il più delle volte il pronlem si trova in spazi o newlines nella sezione di coda. Ho avuto due file XML logicamente identici per il test e il codice non ha scoperto che sono gli stessi. Ma ho appena rimosso il confronto .tail dal codice e ha funzionato come un incantesimo! – PMN

18

Questa funzione di confronto funziona per me:

def elements_equal(e1, e2): 
    if e1.tag != e2.tag: return False 
    if e1.text != e2.text: return False 
    if e1.tail != e2.tail: return False 
    if e1.attrib != e2.attrib: return False 
    if len(e1) != len(e2): return False 
    return all(elements_equal(c1, c2) for c1, c2 in zip(e1, e2)) 
+3

Questa è una soluzione. Assicurati che gli spazi bianchi non interferiscano, ad es. usando 'etree.XMLParser (remove_blank_text = True)'. Migliora evitando di costruire la lista in 'all()'. Nota che 'zip()' funziona poiché 'len()' è stato testato in precedenza. – One

+1

Neat! Questo sembra funzionare indipendentemente dall'ordine degli elementi, anche per gli elementi con gli stessi tagnames. – Fredrik

Problemi correlati