python-lxml: обеспечение определенного порядка для атрибутов

у меня есть сценарий записи XML, который выводит XML для конкретного стороннего инструмента.

я использовал исходный XML в качестве шаблона, чтобы убедиться, что я строю все правильные элементы, но окончательный XML не выглядит как оригинал.

Я пишу атрибуты в том же порядке, но lxml записывает их в своем собственном порядке.

Я не уверен, но я подозреваю, что инструмент 3-й части ожидает, что атрибуты появятся в определенном порядке, и я хотел бы решите эту проблему, чтобы я мог увидеть, если его порядок attrib, который делает его неудачным, или что-то еще.

элемент источник:

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

есть ли способ ограничить порядок, в котором они написаны?

3 ответов


порядок атрибутов и читаемость Как отмечали комментаторы, порядок атрибутов не имеет семантического значения в XML, то есть он не изменяет значение элемента:

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

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

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

существует аналогичная характеристика В SQL, где порядок столбцов не изменяется значение определения таблицы. Атрибуты XML и столбцы SQL являются set (не упорядоченный набор), и поэтому все, что можно "официально" сказать о любой одним из них является наличие атрибута или столбца в наборе.

что сказал, Это, безусловно, делает разницу в читаемости, в каком порядке эти вещи появляются в ситуациях, когда такие конструкции создаются и появляются в тексте (например, исходный код) и должны быть интерпретированы, тщательное упорядочение имеет для меня большой смысл.

типичное поведение парсера

любой синтаксический анализатор 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)

похоже, что 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"/>'

обратите внимание, что при передаче атрибутов с помощью Эт.Конструктор SubElement (), Python создает словарь аргументов ключевых слов и передает этот словарь в lxml. Это теряет любой порядок, который вы имели в исходном файле, так как словари Python неупорядочены (или, скорее, их порядок определяется строковыми хэш-значениями, которые могут отличаться от платформы к платформе или, по сути, от выполнения к исполнение.)


OrderedDict атрибутов

начиная с lxml 3.3.3 (возможно, также в более ранних версиях) вы можете передать OrderedDict атрибуты к lxml.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","")]))

обратите внимание, что API ElementTree (xml.etree.ElementTree) делает не сохранить порядок атрибутов, даже если указать OrderedDict до xml.etree.ElementTree.(Sub)Element конструктор!

UPDATE: также обратите внимание, что с помощью **extra параметр lxml.etree.(Sub)Element конструктор для указания атрибутов does не сохранить атрибут:

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