Title: How to create an XML string without a root node?
Tags: javascript;typescript
Question: I am using TypeScript to create a JavaScript function. The function will need to return an XML string which contains keys and values, the values coming from the function's parameters. I would like it to be done safely, for example Terms &amp; Conditions would need encoding to Terms &amp;amp; Conditions. I have seen the DOMParser is recommended for processing XML.
My function currently looks like this:
createDocumentXml(base64Document: string, category: string, documentName: string, documentExtension: string, userId: number, documentSizeBytes: number): string {
let xmlTemplate =
'<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;' +
'<active&gt;true</active&gt;' +
'<category&gt;</category&gt;' +
'<content&gt;</content&gt;' +
'<createdByID&gt;</createdByID&gt;' +
'<createdDate xsi:nil="true"/&gt;' +
'<description&gt;</description&gt;' +
'<fileExtension&gt;</fileExtension&gt;' +
'<name&gt;</name&gt;' +
'<size&gt;</size&gt;' +
'</document&gt;'
// use a DOM parser to modify the XML safely (i.e. escape any reserved characters)
let parser = new DOMParser();
let xmlDocument = parser.parseFromString(xmlTemplate, 'text/xml');
xmlDocument.getElementsByTagName('category')[0].textContent = category;
xmlDocument.getElementsByTagName('content')[0].textContent = base64Document;
xmlDocument.getElementsByTagName('createdByID')[0].textContent = userId.toString();
xmlDocument.getElementsByTagName('description')[0].textContent = documentName;
xmlDocument.getElementsByTagName('fileExtension')[0].textContent = documentExtension;
xmlDocument.getElementsByTagName('name')[0].textContent = documentName;
xmlDocument.getElementsByTagName('size')[0].textContent = documentSizeBytes.toString();
let serializer = new XMLSerializer();
return serializer.serializeToString(xmlDocument);
When called, it returns a string such as this:
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
<active&gt;true</active&gt;
<category&gt;Correspondence\Emails</category&gt;
<content&gt;ZmlzaCAmIGNoaXBzIQ==</content&gt;
<createdByID&gt;6627774</createdByID&gt;
<createdDate xsi:nil="true"/&gt;
<description&gt;Terms &amp;amp; Conditions</description&gt;
<fileExtension&gt;docx</fileExtension&gt;
<name&gt;Terms &amp;amp; Conditions</name&gt;
<size&gt;12345</size&gt;
</document&gt;
How can I get it to just return the inner XML elements without the document root?
<active&gt;true</active&gt;
<category&gt;Correspondence\Emails</category&gt;
<content&gt;ZmlzaCAmIGNoaXBzIQ==</content&gt;
<createdByID&gt;6627774</createdByID&gt;
<createdDate xsi:nil="true"/&gt;
<description&gt;Terms &amp;amp; Conditions</description&gt;
<fileExtension&gt;docx</fileExtension&gt;
<name&gt;Terms &amp;amp; Conditions</name&gt;
<size&gt;12345</size&gt;
I have tried omitting the root from my xmlTemplate but the DOMParser.parseFromString requires one.
The result from this function is stored and subsequently passed into another function which creates the full XML data (including a root node) by inserting it at the relevant place.
Comment: You don't want XML without a root node. This requests makes no sense. (In other words: Tell us why you think you need that.)

Comment: If you want to insert nodes into XML you *never* do that by string concatenation. You take one XML document and insert nodes from another XML document, via the DOM API (`Document.importNode()`). Doing it any other way is asking for trouble. On a related note: You should not handle XML in serialized form until the very moment you write it to disk or send it over the network.

Comment: I see. At this point it probably makes no difference and you could simply use `string.replace()` to remove the bits you don't want. You are aware that it's not good practice (and you've run into a bug that is caused by doing this kind of thing) so that's a step forward. That awareness is what I was trying to create.

Comment: @Iwrestledabearonce. I have updated the question. This function is used to generate a fragment of XML, which is later inserted into a full XML document at the relevant position.

Comment: @Tomalak I have updated the question. This function needs to create a fragment of XML rather than a full XML document

Comment: @Tomalak Unfortunately I have inherited this code and I cannot easily change the target function at the moment because it used extensively throughout the code. I understand string concatenation is bad. This method originally used string concatenation and had the encoding bug which is why I am changing it to use DOM Parsing. I'm trying to improve the code through small refactorings that are acceptable to the business

Comment: xml standard requires a root node. if the document doesnt have one then it's not valid xml. why would you want to omit it anyway?

Comment: ps, that's called encoding, not escaping.

Comment: not relevant, but did you intentionally exclude the "SO" from psychosomatic? if that was intentional, that's pretty clever lol :P


Here is another answer: Rather than your current return, you might:
return Array
.from(xmlDocument.children[0].children)
.map(function(node) { return serializer.serializeToString(node); })
.join("");

Here is another answer: Since (X)HTML is actually valid XML, you can use regular DOM functions on them.
let serializer = new XMLSerializer();
var str = serializer.serializeToString(xmlDocument);
var div = document.createElement("div");
div.innerHTML = str;
var inner = div.getElementsByTagName('document')[0].innerHTML;
return inner;
https://jsfiddle.net/qchbuo7c/
However, Tomolak is right, it woulld be better to just create the whole document all at once if at all possible.
Comment for this answer: *"HTML is actually a subset of XML"* - that's completely wrong. HTML has many features that are not available in XML. They share an API and the fundamental concept of being markup languages, but they are not subsets of one another.
Comment for this answer: With XHTML the statement is true. However, at this point it would make more sense to simply use the dedicated XML facilities (DOMParser, XMLDocument). Also - the OP's question itself is mis-targeted. They should not even be solving this problem in this particular way in the first place.
Comment for this answer: The other answer also is not what I am talking about. The OP should not try to generate XML without a root node. What method you use to arrive at "XML without a root node" is completely irrelevant - simply because that is the wrong thing to even want. (There is no reason to become all defensive. I'm not attacking you.)
Comment for this answer: @Tomalak - typo, my mistake. fixed it.
Comment for this answer: Youre missing the point trying to be argumentative. Someone else already exolained how to do it with xml functions. My point is that native DOM is just as capable of parsing XML as it is HTML. The answer has value and is valid and i literally stated in the answer that this is not a best case scenario use case.
Comment for this answer: "The OP should not try to generate XML without a root node" agree 100%. We've established this. I put it in my answer.
"The other answer also is not what I am talking about." Please by all means post an answer and explain. This comment is not really relevant to my answer.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 9, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2016:00:11/04/16', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<javascript><typescript>', 'Title': 'How to create an XML string without a root node?', 'ViewCount': 656, 'id': 40423651, 'user_AboutMe': '<p>I have been programming as a hobby since I was 14, initially in Visual Basic and scripted languages such as Perl. I have a computer science degree where I learned abstract and theoretical computer science and the Java programming language. Since I started employment in 2005 I have primarily used the Microsoft technologies such as Microsoft SQL Server and the C# .NET language in an agile environment using the SCRUM methodology.</p>\n<p>My projects have included console based, Windows Forms, and web based applications using ASP.NET MVC and restful Web API often with database persistence such as use of Entity Framework or Redis.</p>\n<p>While primarily working on backend infrastructure I also have experience of client side development using HTML, JavaScript, jQuery and KnockoutJS.</p>\n', 'user_AccountId': 7716645.0, 'user_DisplayName': 'Alsty', 'user_Reputation': 788.0, 'user_Views': 123.0, 'user_id': 5844190.0}}