web-dev-qa-db-ja.com

xml.etree.ElementTreeの同等性のテスト

2つのxml要素の同等性に興味があります。そして、要素のtostringをテストすることが機能することを発見しました。しかし、それはハッキーのようです。

2つのetree要素の同等性をテストするためのより良い方法はありますか?

要素を直接比較する:

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)  # True
24
oneporter

この比較機能は私のために働きます:

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))
28
Itamar

文字列の比較が常に機能するとは限りません。 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

これはバージョンに依存する可能性があります(私はPython 2.7.3 with lxml.etree 2.3.2 on Ubuntu);属性の順序を制御する方法が見つからなかったことを覚えています。 1年ほど前、私がやりたかったとき(読みやすさの理由から)。

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

lxmlとxml.etree.ElementTreeの比較

真実は、それが実装に依存するかもしれないということです。どうやら、lxmlはordered dictなどを使用しており、標準の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

別の質問は、比較するよりも重要でないと考えられるものかもしれません。たとえば、一部のフラグメントには余分なスペースが含まれている可能性があり、気にしないでください。このように、必要に応じて正確に機能するシリアル化関数を作成することをお勧めします。

7
lenz

属性は順序に依存しないため(およびその他の理由)、シリアル化と逆シリアル化はXMLでは機能しません。これらの2つの要素は論理的に同じですが、文字列が異なります。

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

要素の比較を正確に行う方法は注意が必要です。私の知る限り、これを行うためのElementTreeには何も組み込まれていません。私はこれを自分で行う必要があり、以下のコードを使用しました。それは私のニーズには機能しますが、大きな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
4
afaulconbridge

信じられないかもしれませんが、それぞれの子の数がわからず、すべての子を検索に含めたい場合は、2つのノードの比較を処理するのに実際に最適な方法です。

もちろん、デモンストレーションしているような子供がいないノードがある場合は、タグ、属性、およびテールのプロパティを簡単に比較できます。

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を使用することによるこれの大きな利点はわかりません。

3
cwallenpoole

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

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

XMLの場合もほぼ同じですが、「。text」の部分(空白かどうかに関係なく、タグの外側にあるテキスト)を処理(削除?削除?)する必要がある場合があります。

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

2
gb.

金メッキしないでください。あなたが持っているものは良い比較です。最後のXMLはTEXTです。

0
fabrizioM