Skip to content

Instantly share code, notes, and snippets.

@clbarnes
Created March 3, 2025 14:51
Show Gist options
  • Save clbarnes/23f9a6840c991f3202ac883ff1c74bb3 to your computer and use it in GitHub Desktop.
Save clbarnes/23f9a6840c991f3202ac883ff1c74bb3 to your computer and use it in GitHub Desktop.
XML to pydantic model/ JSON
from __future__ import annotations
from xml.etree import ElementTree as ET
from pydantic import BaseModel, Field
class XmlElement(BaseModel):
tag: str = Field(alias="#")
attributes: dict[str, str] = Field(alias="@", default_factory=dict)
children: list[str | XmlElement] = Field(alias="c", default_factory=list)
@property
def value(self) -> str | None:
"""If the element has exactly 1 child, which is text, return that text.
Otherwise, return None.
"""
if len(self.children) == 1 and isinstance(self.children[0], str):
return self.children[0]
return None
def all_text(self) -> str:
j = []
self._all_text(j)
return "".join(j)
def _all_text(self, to_join: list[str]):
for c in self.children:
if isinstance(c, str):
to_join.append(c)
else:
c._all_text(to_join)
@classmethod
def from_str(cls, s: str) -> Self:
return cls.from_etree(ET.fromstring(s))
@classmethod
def from_etree(cls, et: ET.Element) -> Self:
model = cls.model_validate({"#": et.tag, "@": et.attrib, "c": []})
if et.text:
model.children.append(et.text)
for child in et:
model.children.append(XmlElement.from_etree(child))
if child.tail:
model.children.append(child.tail)
return model
def xml_to_json(s: str) -> str:
return XmlElement.from_str(s).model_dump_json(by_alias=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment