Package: lxml-dataclass
LXML-DATACLASSUtility to mirror Python Classes with lxml.etree Elements using class annotations and field descriptors.⚠️ Warning: this package was created to help the owner with big xml file description and manipulation.
Use under your own risk.🆘 Help wanted. If you are insterested in continue the development of this package, send me a PM.Installationpipinstalllxml-dataclassBasic usageLxml-dataclass uses big part of dataclasses implementation with some modifications that allow the implemmentation of two utility methods to the classes that inherit fromElementbase classto_lxml_elementandfrom_lxml_element. Using theelement_fieldfunction allows the metaclass to keep tracking of many lxml.etree attributes.Lets start with some basic examples:fromlxml_dataclassimportElement,element_fieldclassAuthor(Element):__tag__='Author'name:str=element_field('Name')last:str=element_field('LastName',default='Doe')deffullname(self)->str:returnself.name+self.lastauthor=Author('John')author.fullname()# John DoeAs you can see in the given example we defined a class calledAuthorwith the attributesnameandlastas you can see these attributes where defined with their respectives types annotations and the field descriptor functionelement_fieldthis functions accepts almost the same arguments the original dataclassfieldfunction uses except you must give as first argument the tag name the given attribute. The init method is automatically generated and you can overload it.In the example above the tag fornamewill beNameand forlastwill beLastNameNow we will call theto_lxml_element()method to obtain thelxml.etree.Elementrepresenting this objectimportlxml.etreeasETauthor_element=author.to_lxml_element()ET.tostring(author_element)# b'<Author><Name>John</Name><LastName>Doe</LastName></Author>'The Element class also includes another utility method calledto_string_element()which calls theET.tostring()function on the representing element and accepts the same keyword arguments.author.to_string_element(pretty_print=True).decode('utf-8')#<Author>#  <Name>John</Name>#  <LastName>Doe</LastName>#</Author>Attribs and NsmapAs you know all lxml Element accetps theattribandnsmaparguments.
For Element classes you will define those attributes on__attrib__and__nsmap__class or instance attributes and for class attributes you will define them on theelement_fieldmethod with theattribandnsmapkey word arguments.Lets add some attributes:classAuthor(Element):__tag__='Author'__attrib__={'ID':'Test ID'}name:str=element_field('Name',nsmap={None:'suffix'})last:str=element_field('LastName',default='Doe')Before we get the element string we will change the ID attribute.author.__attrib__['ID']='Changed ID'author.to_string_element(pretty_print=True).decode('utf-8')#<Author ID="Changed ID">#  <Name xmlns="suffix">John</Name>#  <LastName>Doe</LastName>#</Author>Class inheritance and compositionInheritance functions exactly the same as dataclasses so you can inherit from otherElementclasses within the rules of dataclass and init generation.Lets create an Element mixin that will add an ID to every instance who inherit it.fromuuidimportUUID,uuid4classHasIDMixin(Element):id:UUID=element_field('Id',default_factory=uuid4)classAuthor(HasIdMixin,Element):__tag__='Author'name:str=element_field('Name')last:str=element_field('LastName',default='Doe')author=Author('John','Mayer')author.to_string_element(pretty_print=True).decode('utf-8')#<Author>#  <Name>John</Name>#  <LastName>Mayer</LastName>#  <Id>a6ff6e02-eeb7-4ca5-8e4d-efedc40ee9ae</Id>#</Author>Now lets create a book element that will be a child of authorclassBook(Element):__tag__='Book'name:str=element_field('Name')pages:int=element_field('Pages',default=50)classAuthor(Element):__tag__='Author'name:str=element_field('Name')last:str=element_field('LastName',default='Doe')book:Book|None=element_field('book',default=None)# Notice the tag is the same as the attribute nameauthor=Author('John')book=Book('My cool book',80)author.book=bookauthor.to_string_element(pretty_print=True).decode('utf-8')#<Author>#  <Name>John</Name>#  <LastName>Doe</LastName>#  <Book>#    <Name>My cool book</Name>#    <Pages>80</Pages>#  </Book>#</Author>Now what if we want an Author to have multiple Books. Then we stablish theis_iterablespecial keyword onelement_fieldfunction as followsclassAuthor(Element):__tag__='Author'name:str=element_field('Name')last:str=element_field('LastName',default='Doe')books:list[Book]=element_field('books',default_factory=list,is_iterable=True)# Notice the tag is the same as the attribute nameNow you could doauthor=Author('John')book_a=Book('My cool book A',80)book_b=Book('My cool book B')author.books.append(book_a)author.books.append(book_b)author.to_string_element(pretty_print=True).decode('utf-8')#<Author>#  <Name>John</Name>#  <LastName>Doe</LastName>#  <Book>#    <Name>My cool book A</Name>#    <Pages>80</Pages>#  </Book>#  <Book>#    <Name>My cool book B</Name>#    <Pages>50</Pages>#  </Book>#</Author>Now you can create really complex xml-class-representations with a simple and known dataclass approach, you can overload the utility methods to suit your needs.