2011-10-26 127 views
18

我對兩個xml元素的等價性感興趣;我發現測試元素的工作原理是有效的;然而,這看起來很詭異。有沒有更好的方法來測試兩個etree元素的等價性?例如:測試xml.etree.ElementTree的等價性

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

h1 == h2 

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

+0

一個比較兩個元素的功能可參見[伊塔馬爾的答案](http://stackoverflow.com/a/24349916/2371522)以下。 – One

回答

0

不要金盤。你有一個很好的比較。最後XML是TEXT。

+0

是的,如果您關心格式化,請轉換爲ET,然後轉儲爲字符串並進行比較。 – Wyrmwood

2

比較複雜結構的常用方法是將它們轉儲到常見的唯一文本表示形式中,並將結果字符串進行比較以求相等。

要比較兩個接收到的json字符串,您需要將它們轉換爲json對象,然後將它們轉換回字符串(使用相同的轉換器)並進行比較。我做了檢查json源,它運行良好。

對於XML,它幾乎相同,但您可能需要處理(strip?remove?)「.text」部分(文本,空白或不可以在標籤外部找到)。

簡而言之,只要確保兩個等效的XML(根據您的上下文)具有相同的字符串表示形式,您的解決方案就不是黑客行爲。

3

如果你不知道每個孩子可能有多少孩子,並且你希望在搜索中包含所有孩子,那麼相信它與否實際上是處理比較兩個節點的最佳方式。

當然,如果你只是有一個像你展示了一個無子女的節點,你可以簡單地比較一下標籤,ATTRIB,和尾性質:

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

我沒有看到任何重大利益但是,這是通過使用tostring。

+0

你也可以根據你的需要拋出文本:'h1.text == h2.text' – bmaupin

+0

這不會比較子元素... – drevicko

7

比較字符串並不總是奏效。對於考慮兩個等效節點,屬性的順序應該不重要。但是,如果您進行字符串比較,則順序顯然很重要。

我不知道,如果這是一個問題或一個功能,但我lxml.etree的版本保留屬性的順序,如果他們是從文件或字符串解析:

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

這可能是版本依賴的(我在Ubuntu上使用Python 2.7.3和lxml.etree 2.3.2);我記得當我想要時(出於可讀性的原因),我無法在一年前找到控制屬性順序的方法。

由於我需要比較不同序列化器產生的XML文件,除了遞歸比較每個節點的標籤,文本,屬性和子元素之外,我沒有其他辦法。當然還有尾巴,如果有什麼有趣的。 LXML和xml.etree.ElementTree

真相的

對比的是,它可能是依賴於實現的。顯然,lxml使用有序的字典或類似的東西,標準的xml.etree。ElementTree的不保留屬性的順序:(是的,換行失蹤,但它是一個小問題。)

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 

另一個問題可能是那些被認爲不重要煥比較。例如,某些片段可能包含多餘的空格,我們不想關心。這樣,編寫一些完全符合我們需要的序列化函數總是更好。

+1

'.dump(...)'返回'None',所以' ET.dump(h1)== ET.dump(h2)'實際上是將'None'與'None'進行比較。 –

4

序列化和反序列化對XML不起作用,因爲屬性不依賴於順序(以及其他原因),例如,這兩個元素在邏輯上是相同的,但是不同的字符串:

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

確切地說,如何做一個元素比較是棘手的。據我所知,元素樹中沒有任何內容可以爲你做到這一點。我需要自己做這個,並使用下面的代碼。它適用於我的需求,但它不適用於大型XML結構,並且不夠快速或高效!這是一個排序函數而不是一個相等函數,所以0的結果是相等的,其他的不是。用True或False返回函數來包裝它,這是讀者的練習!

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

比較兩個XML文件時出現問題的主要原因是與上面所述的格式不同。而且,大多數時候,該子句位於尾部的空格或換行符中。我有兩個邏輯上相同的XML文件用於測試,代碼沒有發現它們是相同的。但是,我只是刪除了代碼中的.tail比較,並且它像一個魅力一樣工作! – PMN

18

這個比較功能對我的作品:

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

這是一個解決方案。確保空白不會干擾,例如通過使用'etree.XMLParser(remove_blank_text = True)'。通過避免在'all()'中建立列表來改進。請注意''zip()'因爲以前測試過'len()'。 – One

+1

整潔!無論元素順序如何,這似乎都可以工作,即使對於具有相同標記名的元素也是如此。 – Fredrik