Hi Tobias,

> lxml.objectfy lets us define custom types by means of objectify.PyType
> and ObjectifiedDataElement sub classes, e.g. as in [0]. It's nice how
> they map to XSD and automatically convert between python type and XML
> representation.
>
> I understand how it works for scalar leaf nodes. But does
> objectify.PyType work for structured/nested types as well? Or is PyType
> the wrong tool and one should better head over to "Generating XML with
> custom classes" [1] and "Custom element class lookup" [2]? Or is it not
> possible at all?

It's not really suitable for structured types since the __setattr__ mechanics
and PyType registry/lookup mechanisms basically put a text representation of
the assigned value plus a type annotation attribute into the "underlying" XML
tree.

See https://lxml.de/objectify.html#how-data-types-are-matched and https://
github.com/lxml/lxml/blob/ac829d561c0bf71fb8cc704305ffc18bd26c6abb/src/lxml/
objectify.pyx#L491
for most there's to know about this.

That said your options depend on how the parsing-from-XML and setting-objects-
in-python-then-serialize-to-XML  behaviour should "mirror" for your use case.

> Simple imaginary code:
>
> ---snip---
> from collections import namedtuple
> from lxml import etree, objectify
>
> # A structured python type
> MyStructuredThing = namedtuple('MyStructuredThing', 'a b')
>
> # some custom code and registration like with objectify.PyType here
> # ...
>
> root = objectify.Element("root")
> # magically take python type and construct tree + leaf elements
> automagically
> root.mystructuredthing = MyStructuredThing(1, 2)
> etree.tostring(root, pretty_print=True)

Since a namedtuple is still a tuple this would trigger special-cased sequence
assignment (details here: https://github.com/lxml/lxml/blob/
ac829d561c0bf71fb8cc704305ffc18bd26c6abb/src/lxml/objectify.pyx#L474 )

>>> root = objectify.Element('root')
>>> root.x = (1, 2, 3)
>>> print(etree.tostring(root))
b'<root xmlns:py="http://codespeak.net/lxml/objectify/pytype";
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xmlns:xsd="http
://www.w3.org/2001/XMLSchema" py:pytype="TREE"><x py:pytype="int">1</x><x
py:pytype="int">2</x><x py:pytype="int">3</x></root>

Of course, you could always override __setattr__ in a custom subclass and
special-case your structured datatype:

>>> E = objectify.E
>>> class MyElement(objectify.ObjectifiedElement):
...     def __setattr__(self, name, value):
...         if isinstance(value, Structured):
...             value = E.structured(E.a(value.a), E.b(value.b))
...         objectify.ObjectifiedElement.__setattr__(self, name, value)
...
>>> root = MyElement('root')
>>> root.structured = Structured(a=1, b=2)
>>> print(etree.tostring(root))
b'<MyElement>root<structured xmlns:py="http://codespeak.net/lxml/objectify/
pytype" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
xmlns:xsd="http://www.w3.org/2001/XMLSchema";><a py:pytype="int">1</a><b
py:pytype="int">2</b></structured></MyElement>'

That won't get you creation of Structured objects when parsing - you'd need
custom element class lookup for such stuff, and basically an Element class
(distinct from your namedtuple) that represents your structured datatype.
Note how parsing from XML doesn't give you built-in Python datatypes but
objectified representatives that behave (very much) like the built-ins.
(See Advanced element class lookup here: 
https://lxml.de/objectify.html#how-data-types-are-matched)

I'd probably forgo all this and simply use the glorious E-Factory to create
structured data in assignments where needed:

>>> root = objectify.Element('root')
>>> root.structured = E._(E.a(1), E.b(2))
>>> print(etree.tostring(root))
b'<root xmlns:py="http://codespeak.net/lxml/objectify/pytype";
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xmlns:xsd="http://
www.w3.org/2001/XMLSchema" py:pytype="TREE"><structured><a py:pytype="int">1</
a><b py:pytype="int">2</b></structured></root>'
>>>

(You can even build a vocabulary if you wish, like some mini-DSL, see:
https://lxml.de/tutorial.html#the-e-factory)

I.e. create the structured ObjectifiedElement "from the outside", not
implicitly in the assignment.

Best,
Holger





_______________________________________________
lxml - The Python XML Toolkit mailing list -- lxml@python.org
To unsubscribe send an email to lxml-le...@python.org
https://mail.python.org/mailman3/lists/lxml.python.org/
Member address: arch...@mail-archive.com

Reply via email to