How to use XPATH / conditions in wrapped()
Firstly thanks for this amazing package!
I am trying to use an XPATH expression in my wrapped field definition, but it seems like it is not applied correctly.
Below is an example where I want to map the product_attribute/value element to the name attribute in my Class only if the product_attribute/type is name
When using lxml this can be achieved via the XPATH expression product_attribute/[type='name']/value. When I try the same expression in the wrapped function, it does not work.
Now my questions:
- Are XPATHs even supported?
- How could I apply a condition in an other way?
from pydantic_xml import BaseXmlModel, wrapped
from lxml import etree
if __name__ == "__main__":
xml_str = """
<product>
<product_attribute>
<type>name</type>
<value>Product Name</value>
</product_attribute>
<product_attribute>
<type>price</type>
<value>1</value>
</product_attribute>
</product>
"""
# using lxml & XPATH
tree = etree.fromstring(xml_str)
print("Product Name: ", tree.findtext("product_attribute/[type='name']/value"))
# using pydantic-yml without XPATH
class Product(BaseXmlModel, search_mode="ordered", tag="product"):
name: str = wrapped("product_attribute/value")
print("Product Name without condition: ", Product.from_xml(xml_str).name)
# using pydantic-yml with condition
class Product(BaseXmlModel, search_mode="ordered", tag="product"):
name: str = wrapped("product_attribute/[type='name']/value")
print("Product Name with condition: ", Product.from_xml(xml_str).name)
Many thanks in advance!
@mehmax Hi,
Thanks for your feedback.
The library doesn't support xpath expressions as well as wrapped.
You could declare attributes as a list and define a property:
class ProductAttribute(BaseXmlModel):
type: str = element()
value: str = element()
class Product(BaseXmlModel, search_mode="ordered", tag="product"):
attributes: list[ProductAttribute] = element("product_attribute")
@property
def name(self) -> str:
return next(attr for attr in self.attributes if attr.type == 'name').value
product = Product.from_xml(xml_str)
print("Product Name: ", product.name)
or define a map if the attributes are not supposed to be modified:
class ProductAttribute(BaseXmlModel):
model_config = pydantic.ConfigDict(frozen=True)
type: str = element()
value: str = element()
class Product(BaseXmlModel, search_mode="ordered", tag="product"):
attributes_list: tuple[ProductAttribute, ...] = element("product_attribute")
@functools.cached_property
def attributes(self) -> dict[str, str]:
return {attr.type: attr.value for attr in self.attributes_list}
product = Product.from_xml(xml_str)
print("Product Name: ", product.attributes['name'])