TL;DR you might want shr-dom-print.
I wanted to write this down because it's kind of non-obvious.
To parse XML / HTML text into nodes, there are libxml-parse-xml-region and libxml-parse-html-region functions which return nodes that can be manipulated with, say, functions from dom.el. But after you got the right parse tree and want to write it out back into a text with XML tags, neither xml.c nor xml.el offer an option for that.
xml.el has an xml-print function, but it seems to expect a parse tree that strictly follows XML (and fails on text nodes, for instance). This may be no fault of xml.el (my need is more with processing loose XML-ish text; so, basically, SGML).
The “parse tree → strings” action has a bunch of names, and in Elisp where everything is its own library it's not even consistent within the language:
json.elcalls itjson-encode,json.ccalls itjson-serialize(on my system there's alsoundo-tree,projectile,markdown.el)yaml.elcalls ityaml-encode,xml.elcalls itxml-print,- for Elisp s-expressions there are
printorprin1(but you can also useformat)
…so it's kind of hard to search for.
The function that best matched my uses was shr-dom-print, which takes a parse tree accepted by dom.el and writes its XML representation into the current buffer. To write/print/encode a parse tree into valid XML, maybe shr-dom-to-xml also helps.