2011-10-26 21 views
18

私は2つのxml要素の等価性に興味があります。私は要素のtostringのテストが動作することを発見しました。しかし、それはハッキーのようです。二つの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

2つの要素を比較する機能は、以下の[Itamarの答え](http://stackoverflow.com/a/24349916/2371522)で見つけることができます。 – One

答えて

0

金ではないプレートを行います。あなたが持っているものは良い比較です。最後のXMLではTEXTです。

+0

はい。書式設定が心配な場合は、ETに変換してから文字列にダンプして比較してください。 – Wyrmwood

2

複雑な構造を比較する通常の方法は、それらを共通の一意のテキスト表現にダンプし、結果の文字列を同等かどうか比較することです。

受信した2つのjson文字列を比較するには、それらをjsonオブジェクトに変換してから、同じコンバーターを使用して文字列に変換して比較します。私はjsonのフィードをチェックするためにそれをしました、それはうまくいきます。

XMLの場合、ほとんど同じですが、 ".text"部分(タグの外にあるテキスト、空白かどうか)を処理(strip?remove?)する必要があります。

つまり、2つの同等のXML(コンテキストに応じて)が同じ文字列表現を持つことを確認している限り、ソリューションはハックではありません。

3

実際には、どれくらいの子供がいるかもわからず、すべての子供を検索に含める場合は、2つのノードを比較するのが実際には最良の方法です。もちろん

、あなたは単にあなたが実証されているような子どもがノードを持っている場合、あなたは、単にタグ、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

これはエレメントchildrenを比較しません... – drevicko

7

文字列の比較が常に機能するとは限りません。属性の順序は、2つのノードを同等と見なしても問題ありません。しかし、文字列の比較を行う場合、順序は明らかに重要です。

私はそれが問題や機能であるかどうかわからないんだけど、彼らは、ファイルや文字列から解析された場合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でlxml.etree 2.3.2でPython 2.7.3を使用しています)。 1年前に属性の順序を制御する方法が見つからないことを覚えています(可読性の理由から)。

異なるシリアライザによって生成されたXMLファイルを比較する必要があるため、タグ、テキスト、属性、およびすべてのノードの子を再帰的に比較する以外の方法はありません。もちろん、そこに興味深いものがあれば、尾ももちろんです。

lxmlの比較とxml.etree.ElementTree

真実は、それは実装依存する可能性があるということです。どうやら、lxmlは、標準のxml.etreeというような、命令付きのdictなどを使用しています。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 

もう一つの問題は重要でないwhan比較するとみなされるものであってもよいです。たとえば、一部のフラグメントに余分なスペースが含まれていて、気にしたくない場合があります。このようにして、私たちが必要とする正確に機能するシリアライズ関数を書く方が良いでしょう。

+1

' .dump(...) 'は' None'を返します。 ET.dump(h1)== ET.dump(h2) 'は' None'と 'None'を実際に比較しています。 –

4

属性が順序に依存しないため(その他の理由で)、XMLのシリアライズとデシリアライズは機能しません。これらの2つの要素は論理的には同じですが、異なる文字列です:

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

要素の比較方法は厳密です。私が知る限り、Element Treeには何も組み込まれていません。私はこれを自分で行う必要があり、以下のコードを使用しました。私のニーズに対応していますが、大規模な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

2つのXMLファイルを比較する際の問題の主な原因は、上で述べたような異なるフォーマットです。そして、ほとんどの場合、発音部分はテールセクションのスペースや改行にあります。私はテストのために2つの論理的に同一の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()'でリストを作成しないようにして、改善してください。以前は 'len()'がテストされていたので、 'zip()'が動作することに注意してください。 – One

+1

ニート!これは、たとえ同じタグ名を持つ要素であっても、要素の順序に関係なく動作するようです。 – Fredrik