Copyright © 2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C standards and drafts index.
This document was published by the Web Applications Working Group as an Editor's Draft.
Publication as an Editor's Draft does not imply endorsement by W3C and its Members.
This is a draft document and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to cite this document as other than a work in progress.
This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent that the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 18 August 2025 W3C Process Document.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
This specification depends on the Infra Standard. [INFRA]
The IDL fragments in this specification must be interpreted as required for conforming IDL fragments, as described in the Web IDL specification. [WEBIDL]
Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and terminate these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.
Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)
User agents may impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations.
When a method or an attribute is said to call another method or attribute, the user agent must invoke its internal API for that attribute or method so that e.g. the author can't change the behavior by overriding attributes or methods with custom properties or functions in ECMAScript. [ECMA-262]
If an algorithm calls into another algorithm, any exception that is thrown by the latter (unless it is explicitly caught), must cause the former to terminate, and the exception to be propagated up to its caller.
Vendor-specific proprietary extensions to this specification are strongly discouraged. Authors must not use such extensions, as doing so reduces interoperability and fragments the user base, allowing only users of specific user agents to access the content in question.
If vendor-specific extensions are needed, the members should be prefixed by vendor-specific strings to prevent clashes with future versions of this specification. Extensions must be defined so that the use of extensions neither contradicts nor causes the non-conformance of functionality defined in the specification.
When vendor-neutral extensions to this specification are needed, either this specification can be updated accordingly, or an extension specification can be written that overrides the requirements in this specification. Such an extension specification becomes an applicable specification for the purposes of conformance requirements in this specification.
A document object model (DOM) is an in-memory representation of various types of Nodes
where each Node is connected in a tree. The [HTML] and [DOM] specifications describe
DOM and its Nodes in greater detail.
Parsing is the term used for converting a string representation of a DOM into an actual DOM, and Serializing is the term used to transform a DOM back into a string. This specification concerns itself with defining various APIs for both parsing and serializing a DOM.
Element.innerHTML API is a common way to both
parse and serialize a DOM (it does both). If a particular Node has the following in-memory
DOM:
HTMLDivElement (nodeName: "div")
┃
┣━ HTMLSpanElement (nodeName: "span")
┃ ┃
┃ ┗━ Text (data: "some ")
┃
┗━ HTMLElement (nodeName: "em")
┃
┗━ Text (data: "text!")
And the HTMLDivElement node is stored in a variable myDiv,
then to serialize myDiv's children simply get (read) the
Element.innerHTML property (this triggers the serialization):
var serializedChildren = myDiv.innerHTML;
// serializedChildren has the value:
// "<span>some </span><em>text!</em>"
To parse new children for myDiv from a string (replacing its existing
children), simply set the Element.innerHTML property (this triggers
parsing of the assigned string):
myDiv.innerHTML = "<span>new</span><em>children!</em>";
This specification describes two flavors of parsing and serializing: HTML and XML (with XHTML being a type of XML). Each follows the rules of its respective markup language. The above example shows HTML parsing and serialization. The specific algorithms for HTML parsing and serializing are defined in the [HTML] specification. This specification contains the algorithm for XML serializing. The grammar for XML parsing is described in the [XML10] specification.
Round-tripping a DOM means to serialize and then immediately parse the serialized
string back into a DOM. Ideally, this process does not result in any data loss with respect to the
identity and attributes of the Node in the DOM.
Round-tripping is especially tricky for an XML serialization, which must be concerned with
preserving the Node's namespace identity in the serialization (wereas namespaces are
ignored in HTML).
Element (nodeName: "root")
┃
┗━ HTMLScriptElement (nodeName: "script")
┃
┗━ Text (data: "alert('hello world')")
An XML serialization must include the HTMLScriptElement's namespace in order to
preserve the identity of the script element, and to allow the serialized string to
round-trip through an XML parser. Assuming that root
is in a variable named root:
var xmlSerialization = new XMLSerializer().serializeToString(root);
// xmlSerialization has the value:
// "<root><script xmlns="http://www.w3.org/1999/xhtml">alert('hello world')</script></root>"
DOMParser interfaceThe definition of DOMParser has moved to the HTML Standard.
XMLSerializer interfaceThe definition of XMLSerializer has moved to the HTML Standard.
The definition of Element.innerHTML has moved to the HTML Standard.
Element interfaceThe definition of Element.outerHTML has moved to the HTML Standard.
The definition of Element.insertAdjacentHTML has moved to the HTML Standard.
Range interfaceThe definition of Range.createContextualFragment() has moved to the HTML Standard.
The definition of fragment parsing algorithm has moved to the HTML Standard.
The definition of fragment serializing algorithm has moved to the HTML Standard.
An XML serialization differs from an HTML serialization in the following ways:
Elements and attributes will always be serialized such
that their namespace is preserved. In some cases this means that an existing
prefix, prefix declaration attribute or default namespace declaration attribute
might be dropped, substituted or changed. An HTML serialization does not attempt to
preserve the namespace.
Elements not in the HTML namespace containing no children, are
serialized using the empty-element tag syntax (i.e., according to the XML
EmptyElemTag production).
Otherwise, the algorithm for producing an XML serialization is designed to produce a serialization that is compatible with the HTML parser. For example, elements in the HTML namespace that contain no children are serialized with an explicit begin and end tag rather than using the empty-element tag syntax.
To produce an XML serialization of a Node node given
a boolean require well-formed, run the following steps:
Let namespace be null.
namespace tracks the XML serialization algorithm's current
default namespace. It is changed when either an Element has a default namespace
declaration, or the algorithm generates a default namespace declaration for the Element to
match its own namespace.
The algorithm assumes no namespace (null) to start.
xml" to prefix map given the XML namespace.
Let prefix index be 1.
prefix index is used to generate a new unique prefix when no suitable existing namespace prefix is available to serialize a node's namespace (or the namespace of one of the attributes in node's attribute list). See the generate a prefix algorithm.
InvalidStateError" DOMException.
The XML serialization algorithm, given
a Node node,
a string namespace,
a namespace prefix map prefix map,
a mutable reference to an integer prefix index,
and
a boolean require well-formed,
must run the following steps:
If node's interface is:
Element
Document
Comment
CDATASection
Text
DocumentFragment
DocumentType
ProcessingInstruction
Attr
TypeError. Only Nodes and Attrs can be serialized by this algorithm.
Each of the above referenced algorithms are detailed in the sections that follow.
The algorithm for XML serializing an Element node, given
an Element node,
a string namespace,
a namespace prefix map prefix map,
a mutable reference to an integer prefix index,
and
a boolean require well-formed,
must run the following steps:
:" (U+003A COLON) or does not match the XML Name production, then
throw an exception.
(The serialization of node would not be well-formed.)
<" (U+003C LESS-THAN SIGN).
Let local prefixes map be « » (an ordered map from strings to strings).
Its keys will be prefixes, and its values will be namespaces. In this map, the null namespace is represented by the empty string.
This map is local to each element. It is used to ensure there are no conflicting
prefixes if a new namespace prefix attribute needs to be
generated.
It is also used to enable skipping of duplicate prefix definitions when
writing an element's attributes: the map
allows the algorithm to distinguish between a prefix in the namespace prefix map that
might be locally-defined (to the current Element) and one that is not.
The above step will update map with any found namespace prefix definitions, add the found prefix definitions to local prefixes map and return the value of a default namespace attribute (which can be empty) if one exists. Otherwise it returns null.
inherited ns will be passed down as the namespace argument when serializing node's children.
node is in the current default namespace. The steps below
serialize node without a prefix (even if it had one), and drop any default
namespace declaration. An exception is made if node is in the XML namespace,
in which case the "xml:" prefix is used; this prefix does not need to be declared and no
effort is made to declare it.
xml:" and node's local name to qualified name.
inherited ns is not equal to ns; node's own namespace is different from the context namespace. To differentiate node's namespace from the context namespace, the steps below will use a namespace prefix if one is available; if not, they will use or introduce a default namespace declaration.
candidate prefix will be null if no prefix was found that maps
to ns (not even prefix). In that case, this algorithm will
generate a new xmlns attribute and add any new prefix to map below.
xmlns", then:
Element with namespace prefix "xmlns"
will not legally round-trip in a conforming XML parser.
Either node or one of its ancestors defines that candidate prefix maps to node's namespace.
The following could serialize a different prefix than node's existing namespace prefix, if any. However, this only happens if this prefix does not map to the correct namespace, as the retrieving a preferred prefix string algorithm already tried to match the existing prefix.
Suppose that we have the following XML document, parse it, and serialize it.
<root xmlns:x="uri1">
<table xmlns="uri1"/>
</root>If we follow the current specification, the serialization result is:
<root xmlns:x="uri1">
<x:table xmlns="uri1"/>
</root>It's incompatible with Edge, Firefox, Safari, and Chrome 73-.
(Chrome 74 produces the above result, and we're fixing it.)
I think 12.1 in https://w3c.github.io/DOM-Parsing/#xml-serializing-an-element-node should be changed as following:
Original: Let candidate prefix be the result of retrieving a preferred prefix string prefix from map given namespace ns.
Proposed: Let candidate prefix be null if prefix is null and ns equals to local default namespace. Otherwise let candidate prefix be the result of retrieving a preferred prefix string prefix from map given namespace ns.
WPT domparsing/XMLSerializer-serializeToString.html contains a testcase for this behavior.
"Check if start tag serialization does NOT apply the default namespace if its namespace is declared in an ancestor."
:"
(U+003A COLON), and node's local name to qualified name.
It is possible that inherited ns differs from node's namespace.
Any default namespace definitions or namespace prefixes that define the XML namespace are omitted when serializing attributes in node's attribute list.
By this step, there is no namespace or prefix mapping declaration in
node (or any parent Node visited by this algorithm) that defines
a prefix that maps to node's namespace; otherwise the step labelled
Found a suitable namespace prefix would have been followed. Ideally we would use
node's namespace prefix. However, it could be the case that
node already declares its own prefix as mapping to something else than its own
namespace. In that case we will generate a new prefix as a last
resort. In either case, the sub-steps that follow will serialize a new namespace prefix
declaration for the prefix we end up using.
:" (U+003A COLON), and
node's local name to qualified name.
The following serializes a namespace prefix declaration for prefix which was just added to map.
" (U+0020 SPACE);
xmlns:";
="" (U+003D EQUALS SIGN, U+0022 QUOTATION MARK);
"" (U+0022 QUOTATION MARK).
At this point, the namespace for this node still needs to be serialized, but there's no namespace prefix (or candidate prefix) available; the following uses the default namespace declaration to define the namespace—optionally replacing an existing default declaration if present.
The following serializes the new (or replacement) default namespace definition.
" (U+0020 SPACE);
xmlns";
="" (U+003D EQUALS SIGN, U+0022 QUOTATION MARK);
"" (U+0022 QUOTATION MARK).
local default namespace is ns.
All of the combinations where ns is not equal to inherited ns are handled above such that node will be serialized preserving its original namespace.
area",
"base",
"basefont",
"bgsound",
"br",
"col",
"embed",
"frame",
"hr",
"img",
"input",
"keygen",
"link",
"menuitem",
"meta",
"param",
"source",
"track",
"wbr";
then append the following to markup, in the order listed:
" (U+0020 SPACE);
/" (U+002F SOLIDUS).
/" (U+002F SOLIDUS) to markup
and set skip end tag to true.
>" (U+003E GREATER-THAN SIGN) to markup.
template" (this is a template element), append to
markup the result of XML serializing a DocumentFragment node given
node's template contents (a DocumentFragment),
inherited ns, map, prefix index, and
require well-formed.
This allows template content to round-trip, given the rules for parsing XHTML documents.
Otherwise, for each child of node's children:
</" (U+003C LESS-THAN SIGN, U+002F SOLIDUS);
>" (U+003E GREATER-THAN SIGN).
This following algorithm will update the namespace prefix map with any found namespace prefix definitions, add the found prefix definitions to local prefixes map, and return a local default namespace value defined by a default namespace attribute if one exists. Otherwise it returns null.
When recording the namespace information for a list of attributes attributes, given a namespace prefix map map and an ordered map local prefixes map, run the following steps:
The following conditional steps find namespace prefixes. Only
attributes in the XMLNS namespace are considered (e.g.,
attributes made to look like namespace declarations via
setAttribute("xmlns:pretend-prefix", "pretend-namespace") are not included).
XML namespace definitions in prefixes are completely ignored (in
order to avoid unnecessary work when there might be prefix conflicts).
Elements in the XML namespace are always handled uniformly by prefixing (and
overriding if necessary) the element's local name with the reserved
"xml" prefix.
This step avoids adding duplicate prefix definitions for the same namespace in map. This has the side-effect of avoiding later serialization of duplicate namespace prefix declarations in any descendant nodes.
The empty string is a legitimate return value and is not converted to null.
A namespace prefix map is an ordered map from strings or null to lists of strings.
The keys are namespaces, with null representing no namespace; the values are lists of prefixes that map to that namespace.
An empty namespace prefix map will be created at the start of the
XML serialization algorithm. Whenever a new Element is encountered, the map will be
cloned (copy a namespace prefix map) and new associations will be added for that
Element (primarily in recording the namespace information, but also when adding
new namespace declarations because no prefix is available for a particular namespace).
The last seen prefix for a given namespace is at the end of its respective list. When serializing, the Element's namespace prefix will be used if it is in the list; otherwise the last prefix in the list is used. See retrieve a preferred prefix string for additional details.
To copy a namespace prefix map map:
For each key → value in map:
To retrieve a preferred prefix string preferred prefix (a string or null) from the namespace prefix map map given a namespace ns:
If prefix is not null, then:
For each prefix of candidates:
To check if a string prefix is found in a namespace prefix map map given a namespace ns:
To add a string prefix to a namespace prefix map map given a namespace ns:
The steps in retrieve a preferred prefix string use the list to track the most recently used prefix associated with a given namespace, which will be the prefix at the end of the list. This list can contain duplicates of the same prefix seen earlier (and that's OK).
The XML serialization of the attributes of an Element
element given a namespace prefix map map,
a mutable reference to an integer prefix index,
an ordered map local prefixes map,
a boolean ignore namespace definition attribute, and
a boolean require well-formed, is the result of the following algorithm:
localname set will contain tuples of unique attribute (namespace, local name) pairs, and is populated as each attr is processed. If require well-formed is true, it is used to enforce the well-formed constraint that an element cannot have two attributes with the same namespace and local name. This can occur when two otherwise identical attributes on the same element differ only by their prefix values. If require well-formed is false, localname set is unnecessary.
The XML namespace cannot be redeclared and survive
round-tripping (unless it defines the prefix "xml"). To avoid this
problem, this algorithm always prefixes elements in the XML namespace with
"xml" and drops any related definitions as seen in the above condition.
Element's default namespace attribute is to be skipped);
DOM APIs do allow creation of elements in the XMLNS namespace but with strict qualifications.
xmlns", then set candidate prefix to
"xmlns".
" (U+0020 SPACE);
xmlns:";
="" (U+003D EQUALS SIGN, U+0022 QUOTATION MARK);
"" (U+0022 QUOTATION MARK).
" (U+0020 SPACE) to result.
:" (U+003A COLON).
:" (U+003A COLON) or does not match the XML Name
production or equals "xmlns" and attribute namespace is null, then
throw an exception.
The serialization of attr would not be well-formed.
="" (U+003D EQUALS SIGN, U+0022 QUOTATION MARK);
"" (U+0022 QUOTATION MARK).
When serializing an attribute value given a string or null attribute value and a boolean require well-formed, run the following steps:
&" with "&"
"" with """
<" with "<"
	"

"

"
This matches behavior present in browsers, and goes above and beyond the grammar
requirement in the XML specification's AttValue production by also replacing
">" characters.
To generate a prefix given a namespace prefix map map, a string new namespace, and a mutable reference to an integer prefix index:
ns" and the
current numerical value of prefix index.
https://w3c.github.io/DOM-Parsing/#generating-namespace-prefixes
The algorithm just generates 'ns1', 'ns2', ... without checking existence of generated prefixes.
So, the following example serializes two xmlns:ns1 on child element if we follow the current specification. WPT domparsing/XMLSerializer-serializeToString.html already has a test case ("Check if "ns1" is generated even if the element already has xmlns:ns1.").
const root = (new DOMParser()).parseFromString('<root xmlns:ns2="uri2"><child xmlns:ns1="uri1" xmlns:a0="uri1" xmlns:NS1="uri1"/></root>', 'text/xml').documentElement;
root.firstChild.setAttributeNS('uri3', 'attr1', 'value1');
console.log((new XMLSerializer()).serializeToString(root));
The algorithm should have a loop until a generated prefix is not found.
The algorithm for XML serializing a Document node, given
a Document node,
a string namespace,
a namespace prefix map prefix map,
a mutable reference to an integer prefix index,
and
a boolean require well-formed,
must run the following steps:
For each child of node's children:
This will serialize any number of ProcessingInstruction and Comment
nodes both before and after the document element, as well as at most one
DocumentType node. (Text nodes are not allowed as children of a Document.)
The algorithm for XML serializing a Comment node, given
a Comment node,
and
a boolean require well-formed,
must run the following steps:
--" (two adjacent U+002D HYPHEN-MINUS characters) or ends with a
"-" (U+002D HYPHEN-MINUS) character, then throw an exception.
The serialization of node would not be well-formed.
<!--", node's
data, and "-->".
The algorithm for XML serializing a CDATASection node, given
a CDATASection node,
and
a boolean require well-formed,
must run the following steps:
<![CDATA[",
node's data, and "]]>".
The algorithm for XML serializing a Text node, given
a Text node,
and
a boolean require well-formed,
must run the following steps:
&" in markup by
"&".
<" in markup by
"<".
>" in markup by
">".
The algorithm for XML serializing a DocumentFragment node, given
a DocumentFragment node,
a string namespace,
a namespace prefix map prefix map,
a mutable reference to an integer prefix index,
and
a boolean require well-formed,
must run the following steps:
For each child of node's children:
The algorithm for XML serializing a DocumentType node, given
a DocumentType node,
and
a boolean require well-formed,
must run the following steps:
"" (U+0022 QUOTATION MARK) and a "'" (U+0027 APOSTROPHE),
then throw an exception.
The serialization of node would not be well-formed.
<!DOCTYPE" to markup.
" (U+0020 SPACE) to markup.
" (U+0020 SPACE);
PUBLIC";
" (U+0020 SPACE);
" (U+0020 SPACE);
SYSTEM".
" (U+0020 SPACE);
>" (U+003E GREATER-THAN SIGN) to markup.
The serialization of the ID id is the result of the following steps:
"" (U+0022 QUOTATION MARK), let q be
"'" (U+0027 APOSTROPHE), and let q be """ (U+0022 QUOTATION MARK)
otherwise.
The algorithm for XML serializing a ProcessingInstruction node, given
a ProcessingInstruction node,
and
a boolean require well-formed,
must run the following steps:
:" (U+003A COLON)
character or is an ASCII case-insensitive match for "xml", then
throw an exception.
The serialization of node would not be well-formed.
?>" (U+003F QUESTION MARK,
U+003E GREATER-THAN SIGN), then throw an exception.
The serialization of node would not be well-formed.
template's template contents
AttValue,
Char,
EmptyElemTag,
Name and
PubidChar productions
We acknowledge with gratitude the original work of Ms2ger and others at the WHATWG, who created and maintained the original DOM Parsing and Serialization Living Standard upon which this specification is based.
Thanks to C. Scott Ananian, Victor Costan, Aryeh Gregor, Anne van Kesteren, Arkadiusz Michalski, Simon Pieters, Henri Sivonen, Josh Soref and Boris Zbarsky, for their useful comments.
Special thanks to Ian Hickson for first defining the innerHTML and outerHTML attributes, and the insertAdjacentHTML method in [HTML] and his useful comments.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: