Разбор подузла с PyXB

Используя PyXB, я хотел бы сериализовать подчиненный узел и затем иметь возможность проанализировать его обратно. Наивный способ не работает, потому что подузел не является допустимым корневым элементом в соответствии со схемой.

Моя схема:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="root" type="Root"/>

  <xsd:complexType name="Root">
    <xsd:sequence>
      <xsd:element name="item" maxOccurs="unbounded" type="Item"/>
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="Item">
    <xsd:sequence>
      <xsd:element name="val"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

И образец XML:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <item>
        <val>1</val>
    </item>
    <item>
        <val>2</val>
    </item>
    <item>
        <val>3</val>
    </item>
</root>

Мне нужно иметь возможность сериализовать определенный элемент, а затем загрузить его обратно. Что-то вроде этого:

>>> root = CreateFromDocument(sample)
# locate a sub node to serialize
>>> root.item[1].toxml()
'<?xml version="1.0" ?><item><val>2</val></item>'
# load the sub node, getting an Item back
>>> sub_node = CreateFromDocument('<?xml version="1.0" ?><item><val>2</val></item>')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "binding.py", line 63, in CreateFromDocument
    instance = handler.rootObject()
  File "pyxb/binding/saxer.py", line 285, in rootObject
    raise pyxb.UnrecognizedDOMRootNodeError(self.__rootObject)
pyxb.exceptions_.UnrecognizedDOMRootNodeError: <pyxb.utils.saxdom.Element object at 0x7f30ba4ac550>

# or, perhaps, some kind of unique identifier:
>>> root.item[1].hypothetical_unique_identifier()
'//root/item/1'
>>> sub_node = CreateFromDocument(sample).find_node('//root/item/1')
<binding.Item object at 0x7f30ba4a5d50>

Это, конечно, не работает, потому что item не может быть корневым узлом согласно схеме. Есть ли способ разобрать только поддерево, вернув вместо него Предмет?

В качестве альтернативы, есть ли способ уникальной идентификации подузла, чтобы я мог найти его позже?

2 ответа

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

Хотя я изначально думал о поддержке чего-то вроде XPath, оно так и не было реализовано, и при этом нет стандартного уникального идентификатора, который несет структурную информацию. Если вам нужно пометить элемент-член, чтобы вы могли удалить его, а затем вернуть его туда, откуда он пришел, вы можете просто назначить объекту дополнительные свойства и использовать их на уровне приложения; например:

e = root.item[1]
e.__mytag = '//root/item/1'

Затем вы могли бы написать функцию, которая обходит дерево объектов в поисках совпадения. Такой атрибут, конечно, останется связанным только с этим экземпляром, поэтому впоследствии присваивать другой объект root.item[1] не будет автоматически наследовать тот же атрибут.

В итоге я сделал это, используя начальную строку и номер столбца элемента для его идентификации.

Я добавил этот миксин ко всем своим элементам:

class IdentifierMixin(object):
    """
    Adds an identifier property unique to this node that can be used to locate
    it in the document later.
    """
    @property
    def identifier(self):
        return '%s-%s' % (self._location().lineNumber, self._location().columnNumber)

А затем использовал эту функцию для поиска узлов позже:

def find_by_identifier(root, identifier):
    # BFS over the tree because usually the identifier we're looking for will
    # be close to the root.
    stack = collections.deque([root])
    while stack:
        node = stack.popleft()
        if node.identifier == identifier:
            return node
        stack.extend(node.content())
Другие вопросы по тегам