[Question] Combining multiple tags into a single attribute
Digging through the documentation and code, I was unable to figure out if the following was possible, and if so, what the best approach would be. Supposing I had an xml snippet that looked like:
<system>
<A>
<row>
<item>1.0</item>
<item>0.0</item>
<item>0.0</item>
</row>
<row>
<item>0.0</item>
<item>2.2</item>
<item>3.3</item>
</row>
<row>
<item>0.0</item>
<item>4.4</item>
<item>5.5</item>
</row>
</A>
</system>
I'm trying to map A to something like:
class Example(BaseXmlModel, tag="system"):
A: list[list[float]]: element(default_factory=list)
where ex = Example.from_xml(doc.xml) would have
ex.A == [[1.0, 0.0, 0.0], [0.0, 2.2, 3.3], [0.0, 4.4, 5.5]]
The xml_field_validator used in the custom xml serialization example only really seems capable of mapping one tag to a single attribute. Any suggestions on how to do this, as well as the reverse serialization step would be greatly appreciated.
I'm not sure if there is a better way to solve this, but I just ended up explicitly modeling A, and then keeping a non-exporting field to hold the nested list:
import pydantic_xml as px
from pydantic_xml import BaseXmlModel
class Row(BaseXmlModel, tag='row'):
items: list[float] = px.element(tag='item')
class Amat(BaseXmlModel, tag='A'):
rows: list[Row] = px.element(tag='row')
class System(BaseXmlModel, tag="system"):
A_data: Amat = px.element(tag='A')
_A: list[list[float]] | None = None
@property
def A(self) -> list[list[float]]:
if self._A is None:
self._A = [[item for item in row.items] for row in self.A_data.rows]
return self._A
@A.setter
def A(self, value: list[list[float]]):
self.A_data = Amat(rows=[Row(items=row) for row in value])
self._A = None
@synapticarbors Hi,
you can declare Row as a RootXmlModel and define __iter__ and __eq__ methods:
from typing import Iterator
import pydantic_xml as pxml
xml = '''
<system>
<A>
<row>
<item>1.0</item>
<item>0.0</item>
<item>0.0</item>
</row>
<row>
<item>0.0</item>
<item>2.2</item>
<item>3.3</item>
</row>
<row>
<item>0.0</item>
<item>4.4</item>
<item>5.5</item>
</row>
</A>
</system>
'''
class Row(pxml.RootXmlModel):
root: list[float] = pxml.element(tag='item')
def __iter__(self) -> Iterator[float]:
return iter(self.root)
def __getitem__(self, item: int) -> float:
return self.root[item]
def __eq__(self, other: list[float]) -> bool:
return self.root == other
class System(pxml.BaseXmlModel, tag="system"):
a: list[Row] = pxml.wrapped("A", pxml.element(tag="row", default_factory=list))
sys = System.from_xml(xml)
assert sys.a == [[1.0, 0.0, 0.0], [0.0, 2.2, 3.3], [0.0, 4.4, 5.5]]