XML escaping: what < and & entities are actually for
By the Converterzilla Team
We build privacy-first PDF and image tools that run entirely in your browser. Our team has shipped JavaScript file-processing apps used by thousands every day, and we write here about the libraries, trade-offs and patterns we use.
XML uses a handful of characters as syntax — < and > delimit tags, & starts an entity reference, " and ' quote attribute values. If any of those characters need to appear as literal content rather than as syntax, they have to be escaped, or the document stops being well-formed.
The five predefined entities
| Character | Escape | Why it needs escaping |
|---|---|---|
& | & | Starts every entity reference — a literal ampersand is ambiguous with the start of an escape sequence. |
< | < | Opens a tag — a literal less-than sign would be read as the start of a new element. |
> | > | Closes a tag. Technically only strictly required when it could be confused with a tag close, but escaping it consistently is simpler than reasoning about when it's ambiguous. |
" | " | Terminates a double-quoted attribute value. |
' | ' | Terminates a single-quoted attribute value. |
The most common bug: escaping already-escaped content
Just like URL encoding, XML escaping is not idempotent — running it twice produces the wrong result. If & is escaped to & and then escaped again, it becomes &amp;, which decodes back to the literal string "&" instead of a single ampersand. This usually happens when escaping is applied at two different layers of a pipeline without either one being aware of the other — check whether the text you're about to escape has already been through an XML serializer before escaping it again.
When CDATA is the better tool
For a large block of content that contains lots of special characters — embedded HTML, source code snippets, anything that would otherwise need heavy escaping — wrapping it in <![CDATA[ ... ]]> tells the parser to treat everything inside as literal text with no escaping needed at all, except for the sequence ]]> itself, which can't appear inside a CDATA block (since that's what ends it). For a few special characters scattered through normal prose, entity escaping is simpler; for a large chunk of markup-heavy content, CDATA is usually cleaner.
Our XML escape / unescape tool handles both directions of the five standard entities locally in your browser — useful for a quick round-trip check when you're not sure whether a string has already been escaped.