NV ToolkitFormat · Validate · Convert

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

Learn: XML Tutorial: Structure, Syntax & Survival Skills

  • 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.

Input · XML
<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>
Generated · Python
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).

Python · xmltodict
# 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-75

Help

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

Related tools

Learn the format

Related guides

All guides →