Code Generators
XML to Python Converter
Paste an XML sample, get typed Python models — names, nesting and nullability inferred for you.
In short
Convert XML to Python online: paste a sample and generate typed Python models with serialization helpers — powered by quicktype, in your browser.
By NaveenKumar T · Updated
- Typed models from one sample
- Nested objects become types
- Nullability inferred
Runs entirely in your browser — nothing you paste or open here is uploaded, logged, or stored.How we handle data →
Example
Example: XML order payload to Python
The same order as XML, with data split between attributes (id, status, paid, sku, qty, price) and element text (name, email, total) — the split that decides which Python fields are typed as numbers and which stay strings. The right-hand side is the exact output of this tool.
<order id="ord_9F2KX" status="processing" paid="true">
<customer>
<name>Ada Lovelace</name>
<email>ada@example.com</email>
</customer>
<items>
<item sku="KB-75" qty="1" price="149.99"/>
<item sku="MS-12" qty="2" price="39.5"/>
</items>
<total>228.99</total>
</order>from typing import Any, List, TypeVar, Callable, Type, cast
T = TypeVar("T")
def from_str(x: Any) -> str:
assert isinstance(x, str)
return x
def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
assert isinstance(x, list)
return [f(y) for y in x]
def to_class(c: Type[T], x: Any) -> dict:
assert isinstance(x, c)
return cast(Any, x).to_dict()
def from_float(x: Any) -> float:
assert isinstance(x, (float, int)) and not isinstance(x, bool)
return float(x)
def from_stringified_bool(x: str) -> bool:
if x == "true":
return True
if x == "false":
return False
assert False
def to_float(x: Any) -> float:
assert isinstance(x, (int, float))
return x
class Customer:
name: str
email: str
def __init__(self, name: str, email: str) -> None:
self.name = name
self.email = email
@staticmethod
def from_dict(obj: Any) -> 'Customer':
assert isinstance(obj, dict)
name = from_str(obj.get("name"))
email = from_str(obj.get("email"))
return Customer(name, email)
def to_dict(self) -> dict:
result: dict = {}
result["name"] = from_str(self.name)
result["email"] = from_str(self.email)
return result
class Item:
sku: str
qty: int
price: str
def __init__(self, sku: str, qty: int, price: str) -> None:
self.sku = sku
self.qty = qty
self.price = price
@staticmethod
def from_dict(obj: Any) -> 'Item':
assert isinstance(obj, dict)
sku = from_str(obj.get("sku"))
qty = int(from_str(obj.get("qty")))
price = from_str(obj.get("price"))
return Item(sku, qty, price)
def to_dict(self) -> dict:
result: dict = {}
result["sku"] = from_str(self.sku)
result["qty"] = from_str(str(self.qty))
result["price"] = from_str(self.price)
return result
class Items:
item: List[Item]
def __init__(self, item: List[Item]) -> None:
self.item = item
@staticmethod
def from_dict(obj: Any) -> 'Items':
assert isinstance(obj, dict)
item = from_list(Item.from_dict, obj.get("item"))
return Items(item)
def to_dict(self) -> dict:
result: dict = {}
result["item"] = from_list(lambda x: to_class(Item, x), self.item)
return result
class Order:
customer: Customer
items: Items
total: float
id: str
status: str
paid: bool
def __init__(self, customer: Customer, items: Items, total: float, id: str, status: str, paid: bool) -> None:
self.customer = customer
self.items = items
self.total = total
self.id = id
self.status = status
self.paid = paid
@staticmethod
def from_dict(obj: Any) -> 'Order':
assert isinstance(obj, dict)
customer = Customer.from_dict(obj.get("customer"))
items = Items.from_dict(obj.get("items"))
total = from_float(obj.get("total"))
id = from_str(obj.get("id"))
status = from_str(obj.get("status"))
paid = from_stringified_bool(from_str(obj.get("paid")))
return Order(customer, items, total, id, status, paid)
def to_dict(self) -> dict:
result: dict = {}
result["customer"] = to_class(Customer, self.customer)
result["items"] = to_class(Items, self.items)
result["total"] = to_float(self.total)
result["id"] = from_str(self.id)
result["status"] = from_str(self.status)
result["paid"] = from_str(str(self.paid).lower())
return result
class Root:
order: Order
def __init__(self, order: Order) -> None:
self.order = order
@staticmethod
def from_dict(obj: Any) -> 'Root':
assert isinstance(obj, dict)
order = Order.from_dict(obj.get("order"))
return Root(order)
def to_dict(self) -> dict:
result: dict = {}
result["order"] = to_class(Order, self.order)
return result
def root_from_dict(s: Any) -> Root:
return Root.from_dict(s)
def root_to_dict(x: Root) -> Any:
return to_class(Root, x)
Compare "total" (element text, typed float) with "price" (an attribute, kept as str) — while "qty" and "paid" are converted to int and bool inside from_dict. The root <order> element became a nested Order type under Root.
Learn more
From XML sample to Python types
Hand-writing model classes from an XML payload means deciding, field by field, which parser reads which node — this page automates that decision. quicktype first parses your document into a data model, attributes included, then runs the same type inference it uses for JSON against the result.
The from_dict helpers validate with assert, and python -O strips every assert from the compiled bytecode. Run the models under a normal interpreter, or replace the generated asserts with explicit "if not isinstance(...): raise TypeError" checks before shipping — otherwise a drifted payload passes silently in exactly the environment where the check was supposed to catch it.
How XML elements and attributes become Python types
The XML is parsed into a data model before inference (attributes included, text nodes named "text"), so the root element becomes a nested type: <order> is a field of type Order on the top-level Root rather than the top level itself. Child elements become fields, and repeated elements — the two <item> siblings — are collected into List[Item] under an Items wrapper type that mirrors the <items> container.
Attributes and element text are typed differently, and this matters: element text is type-inferred, so <total>228.99</total> becomes float, while attribute values arrive as strings and Python is one of only two targets that converts them back: price stays str, while qty="1" becomes int and paid="true" becomes bool inside from_dict. Prefer element text for numeric data you want typed, or cast attribute values after decoding. One more trap: a single <item> would not be recognized as a list, so make sure repeated elements appear at least twice in the sample.
Reading the XML in Python with the generated classes
xmltodict.parse gives you a plain dict that root_from_dict accepts directly — pass attr_prefix="" so attributes keep the names the generator saw, instead of arriving as "@id". One catch is worth knowing before you run it: xmltodict returns every value as text, while this page's parser type-infers element text, so the generated code declares total as a float and asserts it. Coerce those fields in a postprocessor (attributes such as qty and paid are converted by the generated code itself, from strings).
# pip install xmltodict
import xmltodict
def numeric(_path, key, value):
# Element text is type-inferred by this page; xmltodict keeps it as text.
return (key, float(value)) if key == "total" else (key, value)
doc = xmltodict.parse(xml_text, attr_prefix="", postprocessor=numeric)
root = root_from_dict(doc)
print(root.order.items.item[0].sku) # KB-75Help
Frequently asked questions
Everything you need to know about the XML to Python.
01How do I generate Python classes from XML?
Paste an XML document and press Convert. It is first parsed into a data model — attributes included — then typed: the root element becomes a nested type, repeated elements become List[Item], and the result downloads as Root.py.
02Can root_from_dict read the output of xmltodict?
Yes, with attr_prefix="" so attributes keep the names the generator saw. One catch: xmltodict returns every value as text, while this page's parser type-infers element text, so the generated code asserts total is a float. Coerce it in a postprocessor before calling root_from_dict.
03Which attribute values are converted to numbers and booleans?
Python is one of two targets that parse attribute strings inside from_dict: qty="1" becomes int and paid="true" becomes bool, while price="149.99" stays str. Element text is inferred at generation time instead, so total is a float.
04Does xmltodict have the same single-element-becomes-an-object trap as the browser parser?
Yes, and it is the more common xmltodict complaint: a document with only one <item> parses items["item"] as a plain dict instead of a list, so root_from_dict's List[Item] assertion fails. Pass force_list={"item"} to xmltodict.parse to pin that key as always-a-list regardless of how many appear, the standard fix for this exact xmltodict behaviour.
Keep working