15

特定のサードパーティツールのXMLを出力するXML書き込みスクリプトがあります。

元のXMLをテンプレートとして使用して、すべての正しい要素を作成していることを確認しましたが、最終的なXMLは元のXMLのようには表示されません。

同じ順序で属性を記述していますが、lxmlは独自の順序で属性を記述しています。

よくわかりませんが、第3部のツールでは属性が特定の順序で表示されることを期待しているので、この問題を解決して、属性の順序が失敗するかどうかを確認したいと思います。

ソース要素:

<FileFormat ID="1" Name="Development Signature" PUID="dev/1" Version="1.0" MIMEType="text/x-test-signature"> 

私のソーススクリプト:

sig.fileformat = etree.SubElement(sig.fileformats, "FileFormat", ID = str(db.ID), Name = db.name, PUID="fileSig/{}".format(str(db.ID)), Version = "", MIMEType = "")

結果のXML:

<FileFormat MIMEType="" PUID="fileSig/19" Version="" Name="Printer Info File" ID="19">

それらが書かれる順序を制限する方法はありますか?

4

6 に答える 6

19

lxmlは、設定した順序で属性をシリアル化するようです。

>>> from lxml import etree as ET
>>> x = ET.Element("x")
>>> x.set('a', '1')
>>> x.set('b', '2')
>>> ET.tostring(x)
'<x a="1" b="2"/>'
>>> y= ET.Element("y")
>>> y.set('b', '2')
>>> y.set('a', '1')
>>> ET.tostring(y)
'<y b="2" a="1"/>'

ET.SubElement()コンストラクターを使用して属性を渡す場合、Pythonはキーワード引数のディクショナリを作成し、そのディクショナリをlxmlに渡すことに注意してください。Pythonの辞書は順序付けされていないため、これによりソースファイルでの順序付けが失われます(つまり、それらの順序は文字列ハッシュ値によって決定されます。文字列ハッシュ値はプラットフォームごとに、または実際には実行ごとに異なる場合があります)。

于 2013-07-15T12:43:42.503 に答える
16

属性のOrderedDict

lxml 3.3.3以降(おそらく以前のバージョンでも)、属性のOrderedDictlxml.etree.(Sub)Elementをコンストラクターに渡すことができ、次を使用すると順序が保持されますlxml.etree.tostring(root)

sig.fileformat = etree.SubElement(sig.fileformats, "FileFormat", OrderedDict([("ID",str(db.ID)), ("Name",db.name), ("PUID","fileSig/{}".format(str(db.ID))), ("Version",""), ("MIMEType","")]))

ElementTree API(xml.etree.ElementTree)は、コンストラクターにを提供しても、属性の順序を保持しないことに注意してください。OrderedDictxml.etree.ElementTree.(Sub)Element

更新:属性を指定するためにコンストラクターの**extraパラメーターを使用しても、属性の順序は保持されないことに注意してください。lxml.etree.(Sub)Element

>>> from lxml.etree import Element, tostring
>>> from collections import OrderedDict
>>> root = Element("root", OrderedDict([("b","1"),("a","2")])) # attrib parameter
>>> tostring(root)
b'<root b="1" a="2"/>' # preserved
>>> root = Element("root", b="1", a="2") # **extra parameter
>>> tostring(root)
b'<root a="2" b="1"/>' # not preserved
于 2014-03-23T19:38:37.720 に答える
6

属性の順序と読みやすさ コメント提供者が述べたように、属性の順序はXMLで意味的な意味を持ちません。つまり、要素の意味は変わりません。

<tag attr1="val1" attr2="val2"/>

<!-- means the same thing as: -->

<tag attr2="val2" attr1="val1"/>

SQLにも同様の特性があり、列の順序によってテーブル定義の意味が変更されることはありません。XML属性とSQL列はセット順序セットではない)であるため、これらのいずれかについて「公式に」言えるのは、属性または列がセットに存在するかどうかだけです。

とは言うものの、これらのものが表示される順序と、このような構造が作成されてテキスト(ソースコードなど)に表示され、解釈される必要がある状況では、人間の読みやすさに間違いなく違いがあります。注意深い順序は私にとって非常に理にかなっています。

典型的なパーサーの動作

属性の順序を重要なものとして扱ったXMLパーサーは、XML標準に準拠していません。それが起こらないという意味ではありませんが、私の経験では確かに珍しいことです。それでも、言及したツールの証明によっては、テストする価値がある可能性があります。

私の知る限りlxml、シリアル化されたXMLに表示される順序属性を指定するメカニズムはありません。そうすると、驚かれることでしょう。

動作をテストするために、テキストベースのテンプレートを作成して、テストするのに十分なXMLを生成することを強く望んでいます。

id = 1
name = 'Development Signature'
puid = 'dev/1'
version = '1.0'
mimetype = 'text/x-test-signature'

template = ('<FileFormat ID="%d" Name="%s" PUID="%s" Version="%s" '
            'MIMEType="%s">')

xml = template % (id, name, puid, version, mimetype)
于 2013-02-17T22:58:09.107 に答える
1

XMLのコンシューマーが正規化されたXMLを期待している注文の問題を見てきました。Canonical XMLは、属性がソートされることを指定します。

名前空間URIを主キー、ローカル名を副キーとして辞書式順序を増やします(空の名前空間URIは辞書式順序で最小です)。(https://www.w3.org/TR/xml-c14n2/のセクション2.6 )

したがって、アプリケーションが正規XMLから取得するような順序を期待している場合、lxmlは、出力するmethod=引数を使用した正規形式での出力をサポートします。(https://lxml.de/api.htmlの見出しC14Nを参照してください)

例えば:

from lxml import etree as ET 
element = ET.Element('Test', B='beta', Z='omega', A='alpha') 
val = ET.tostring(element, method="c14n") 
print(val)
于 2021-03-12T00:04:53.917 に答える
0

新しい文字列をカプセル化する必要があります。これにより、比較時に順序が与えられ、文字列を出力して取得するときに値が与えられます。

次に例を示します。

class S:
    def __init__(self, _idx, _obj):
        self._obj = (_idx, _obj)

    def get_idx(self):
        return self._obj[0]

    def __le__(self, other):
        return self._obj[0] <= other.get_idx()

    def __lt__(self, other):
        return self._obj[0] < other.get_idx()

    def __str__(self):
        return self._obj[1].__str__()

    def __repr__(self):
        return self._obj[1].__repr__()

    def __eq__(self, other):
        if isinstance(other, str):
            return self._obj[1] == other
        elif isinstance(other, S):
            return self._obj[
                       0] == other.get_idx() and self.__str__() == other.__str__()
        else:
            return self._obj[
                0] == other.get_idx() and self._obj[1] == other

    def __add__(self, other):
        return self._obj[1] + other

    def __hash__(self):
        return self._obj[1].__hash__()

    def __getitem__(self, item):
        return self._obj[1].__getitem__(item)

    def __radd__(self, other):
        return other + self._obj[1]

list_sortable = ['c', 'b', 'a']
list_not_sortable = [S(0, 'c'), S(0, 'b'), S(0, 'a')]
print("list_sortable ---- Before sort ----")
for ele in list_sortable:
    print(ele)
print("list_not_sortable ---- Before sort ----")
for ele in list_not_sortable:
    print(ele)
list_sortable.sort()
list_not_sortable.sort()
print("list_sortable ---- After sort ----")
for ele in list_sortable:
    print(ele)
print("list_not_sortable ---- After sort ----")
for ele in list_not_sortable:
    print(ele)

実行結果:

list_sortable ---- Before sort ----
c
b
a
list_not_sortable ---- Before sort ----
c
b
a
list_sortable ---- After sort ----
a
b
c
list_not_sortable ---- After sort ----
c
b
a
dict_sortable ---- After sort ----
a 3
b 2
c 1
dict_not_sortable ---- After sort ----
c 1
b 2
a 3
于 2021-08-31T01:28:45.017 に答える
0

lxmlは内部でlibxml2を使用します。属性の順序が保持されます。つまり、個々の要素について、次のように並べ替えることができます。

x = etree.XML('<x a="1" b="2" d="4" c="3"><y></y></x>')
sorted_attrs = sorted(x.attrib.items())
x.attrib.clear()
x.attrib.update(sorted_attrs)

ただし、すべてを並べ替えたい場合はあまり役に立ちません。それらをすべてソートしたい場合は、c14n2出力メソッド(XML Canonicalisationバージョン2)を使用できます。

>>> x = etree.XML('<x a="1" b="2" d="4" c="3"><y></y></x>')
>>> etree.tostring(x, method="c14n2")
b'<x a="1" b="2" c="3" d="4"><y></y></x>'

これで属性が並べ替えられます。残念ながら、それは無視するという欠点がありpretty_printます。これは、人間が読めるXMLが必要な場合にはあまり良くありません。

If you use c14n2 then lxml will use custom Python serialisation code to write the XML which calls sorted(x.attrib.items() itself for all attributes. If you don't, then it will instead call into libxml2's xmlNodeDumpOutput() function which doesn't support sorting attributes but does support pretty-printing.

Therefore the only solution is to manually walk the XML tree and sort all the attributes, like this:

from lxml import etree

x = etree.XML('<x a="1" b="2" d="4" c="3"><y z="1" a="2"><!--comment--></y></x>')
for el in x.iter(etree.Element):
    sorted_attrs = sorted(el.attrib.items())
    el.attrib.clear()
    el.attrib.update(sorted_attrs)

etree.tostring(x, pretty_print=True)

# b'<x a="1" b="2" c="3" d="4">\n  <y a="2" z="1">\n    <!--comment-->\n  </y>\n</x>\n'
于 2022-01-14T11:19:52.613 に答える