XML in Python, Java and JavaScript

ElementTree and lxml, JAXP and JAXB, DOMParser and XMLSerializer in the browser, and the namespace-aware versus namespace-ignorant API split that causes most confusion.

Python: ElementTree and lxml

import xml.etree.ElementTree as ET

NS = {"s": "urn:example:shop"}

root = ET.fromstring('<order xmlns="urn:example:shop" id="1"><total>19.99</total></order>')

# a namespaced tag is stored as {uri}local, so the bare name finds nothing
print(root.findtext("total"))          # None
print(root.findtext("s:total", NS))    # '19.99'

# writing a document: declare the namespace once, use the prefix
ET.register_namespace("s", "urn:example:shop")
print(ET.tostring(root, encoding="unicode"))
# lxml adds XPath, XSLT and schema validation
from lxml import etree

doc = etree.parse("orders.xml")
nsmap = {"s": "urn:example:shop"}

for total in doc.xpath("//s:order/s:total/text()", namespaces=nsmap):
    print(total)

schema = etree.XMLSchema(etree.parse("order.xsd"))
print(schema.validate(doc), schema.error_log.filter_from_errors())
  • The standard library is namespace-aware but offers no XPath; lxml provides both and is the common production choice.
  • A parser with resolve_entities=True will expand external entities, so keep that setting off for untrusted input.
  • ET.register_namespace affects serialisation only; it does not change the parsed model.
  • Attribute lookup with a namespace requires the qualified name: elem.get("{uri}attr").

Java: JAXP and JAXB

// DOM with hardened defaults
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
f.setFeature("http://xml.org/sax/features/external-general-entities", false);
f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

Document doc = f.newDocumentBuilder().parse(new File("orders.xml"));
XPath xp = XPathFactory.newInstance().newXPath();
xp.setNamespaceContext(new SimpleNamespaceContext(Map.of("s", "urn:example:shop")));
String total = (String) xp.evaluate("string(//s:order/s:total)", doc, XPathConstants.STRING);

// JAXB maps XML to annotated classes; bundled through Jakarta XML Bind now
@XmlRootElement(name = "order", namespace = "urn:example:shop")
public class Order {
    @XmlAttribute public String id;
    @XmlElement(name = "total") public BigDecimal total;
}
NeedAPINote
Tree with queriesDOM plus XPathNamespace aware must be enabled explicitly
StreamingSAX or StAXStAX is a pull model and easier to read
Object mappingJAXBFast to write, brittle on schema change unless configured
Schema validationSchemaFactorySet the schema language explicitly
Hardened parsingFactory featuresEvery factory needs its own configuration

JavaScript in the browser

const text = '<order xmlns="urn:example:shop" id="1"><total>19.99</total></order>';
const doc = new DOMParser().parseFromString(text, "application/xml");

if (doc.querySelector("parsererror")) {
  throw new Error("invalid XML");
}

// namespace-aware query
const NS = "urn:example:shop";
const total = doc.getElementsByTagNameNS(NS, "total")[0].textContent;

// serialise back out; note the closing tag hazard below
const out = new XMLSerializer().serializeToString(doc);

// building a document safely, with no string concatenation
const parser = new DOMParser();
const builder = parser.parseFromString("<root/>", "application/xml");
const el = builder.createElementNS(NS, "s:total");
el.textContent = "19.99";
builder.documentElement.appendChild(el);
  • getElementsByTagName ignores namespaces; getElementsByTagNameNS does not. Mixing them is the usual cause of an empty result.
  • Inline XML in a page is an XML island and is subject to the page's parsing rules, so it must be well-formed at all times.
  • Setting textContent escapes automatically, which is why the DOM API is safer than string building.
  • Injecting serialised XML into a page as markup reintroduces the script-injection problem, so never treat it as trusted HTML.
💡
A namespace-aware API returns a qualified name; a namespace-ignorant one returns only the local name. Choose one model and be consistent, because code that mixes them works by accident until a document with two namespaces appears.

FAQ

Why does findtext return nothing even though the tag exists?
The document uses a default namespace, so the real tag is {uri}total. Pass a namespace map or use the qualified name.
Which Java XML API should new code use?
StAX for streaming and DOM with XPath for small documents. JAXB is convenient but adds a mapping layer that must be maintained as the schema evolves.

Parsing XML in code: DOM, SAX and pull parsers RSS, Atom and web feeds

Last refreshed 2026-09-18.