Skip to content

gh-81055: Support CDATA sections in xml.etree.ElementTree - #156742

Open
serhiy-storchaka wants to merge 2 commits into
python:mainfrom
serhiy-storchaka:gh-81055-cdata
Open

gh-81055: Support CDATA sections in xml.etree.ElementTree#156742
serhiy-storchaka wants to merge 2 commits into
python:mainfrom
serhiy-storchaka:gh-81055-cdata

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Aug 31, 2026

Copy link
Copy Markdown
Member

CDATA is a new factory, like Comment and ProcessingInstruction, which creates a special element serialized as a CDATA section. Its content is character data: it is not escaped, ]]> in it is split between two sections, and it is returned by itertext() and by the "text" serialization method.

TreeBuilder supports the cdata_factory and insert_cdata arguments. When insert_cdata is true, a CDATA section in the input is kept instead of being parsed as text, so a document can be written back unchanged. Expat reports the content of a CDATA section as ordinary character data, but it reports the boundaries, so the builder gets the start_cdata() and end_cdata() methods, which XMLParser calls like comment() and pi(). The pyexpat capsule gets SetCdataSectionHandler for that.

Nothing changes by default: without insert_cdata the content of a CDATA section is still added to the tree as ordinary text.

xml.dom has had CDATASection nodes and Document.createCDATASection() since DOM Level 1; this gives xml.etree the equivalent, and lxml has etree.CDATA for the serialization half.

CDATA is a new factory, like Comment and ProcessingInstruction, which creates
a special element serialized as a CDATA section.  Its content is character
data: it is not escaped, "]]>" in it is split between two sections, and it is
returned by itertext() and by the "text" serialization method.

TreeBuilder gets the cdata_factory and insert_cdata arguments.  When
insert_cdata is set, a CDATA section in the input is kept as such instead of
being parsed as text, so that the document can be written back unchanged.
Expat reports the content of a CDATA section as ordinary character data, but
it reports the boundaries, so the builder gets the start_cdata() and
end_cdata() methods, and XMLParser calls them like comment() and pi().

_set_factories() takes the third factory, which the C implementation needs for
itertext(), and the pyexpat capsule gets SetCdataSectionHandler.
@read-the-docs-community

read-the-docs-community Bot commented Aug 31, 2026

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #34319766 | 📁 Comparing 19d06f7 against main (486b000)

  🔍 Preview build  

3 files changed
± library/xml.etree.elementtree.html
± whatsnew/3.16.html
± whatsnew/changelog.html

@scoder

scoder commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

CDATA can certainly be implemented as a tree element. The reason why I chose not to do this in lxml is that I don't think there is a reason to receive it during tree traversal and iteration. PIs and comments are structural elements with a meaning. CDATA, on the contrary, is really just a way of representing text in the serialised XML. It is only relevant for the parser and for the serialiser. There shouldn't be any actual use for it in the in-memory tree representation. IMHO, making it part of the tree nodes would make it get in the way when processing the tree, without any benefit to make up for it.

@serhiy-storchaka

Copy link
Copy Markdown
Member Author

What is CDATA is intermixed with character data?

<elem>before<![CDATA[<spam> & ham]]>after</elem>

You need a tree element to represent this.

You could do this with an Element with tag=None whose text is a CDATA string, but this will be more complicated than an Element with tag=CDATA, as implemented in this PR.

@scoder

scoder commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What i[f] CDATA is intermixed with character data?

I understand that this is a difference between xml.etree and lxml because lxml always has this information available in the XML C node tree and can handle it in different ways. The normal way is to discard CDATA on the way in, unless users opt for keeping them, in which case they are kept in the tree (for serialisation) but otherwise represented as normal text strings on read, because they are almost never relevant for tree processing.

I think resolving CDATA to regular text in the parser is reasonable default behaviour for xml.etree.
For the way out, the question is whether it's really an important use case to allow mixing string content with CDATA parts. CDATA is not in the XML infoset and it shouldn't be a major restriction to require users to either use regular strings or put a whole text block into a CDATA section, but not parts of it. It's really just about serialisation. And it makes tree processing substantially easier to ignore CDATA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants