0

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 & Conditions would need encoding to Terms & 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">' +
            '<active>true</active>' +
            '<category></category>' +
            '<content></content>' +
            '<createdByID></createdByID>' +
            '<createdDate xsi:nil="true"/>' +
            '<description></description>' +
            '<fileExtension></fileExtension>' +
            '<name></name>' +
            '<size></size>' +
        '</document>'

    // 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">
    <active>true</active>
    <category>Correspondence\Emails</category>
    <content>ZmlzaCAmIGNoaXBzIQ==</content>
    <createdByID>6627774</createdByID>
    <createdDate xsi:nil="true"/>
    <description>Terms &amp; Conditions</description>
    <fileExtension>docx</fileExtension>
    <name>Terms &amp; Conditions</name>
    <size>12345</size>
</document>

How can I get it to just return the inner XML elements without the document root?

<active>true</active>
<category>Correspondence\Emails</category>
<content>ZmlzaCAmIGNoaXBzIQ==</content>
<createdByID>6627774</createdByID>
<createdDate xsi:nil="true"/>
<description>Terms &amp; Conditions</description>
<fileExtension>docx</fileExtension>
<name>Terms &amp; Conditions</name>
<size>12345</size>

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.

Community
  • 1
  • 1
Alsty
  • 817
  • 10
  • 23
  • 3
    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? – I wrestled a bear once. Nov 04 '16 at 13:17
  • ps, that's called encoding, not escaping. – I wrestled a bear once. Nov 04 '16 at 13:18
  • 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.) – Tomalak Nov 04 '16 at 13:18
  • 1
    @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. – Alsty Nov 04 '16 at 13:23
  • @Tomalak I have updated the question. This function needs to create a fragment of XML rather than a full XML document – Alsty Nov 04 '16 at 13:28
  • not relevant, but did you intentionally exclude the "SO" from psychosomatic? if that was intentional, that's pretty clever lol :P – I wrestled a bear once. Nov 04 '16 at 13:29
  • 2
    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. – Tomalak Nov 04 '16 at 13:32
  • @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 – Alsty Nov 04 '16 at 14:04
  • 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. – Tomalak Nov 04 '16 at 14:20

2 Answers2

0

Rather than your current return, you might:

return Array
    .from(xmlDocument.children[0].children)
    .map(function(node) { return serializer.serializeToString(node); })
    .join("");
JonSG
  • 10,542
  • 2
  • 25
  • 36
0

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.

I wrestled a bear once.
  • 22,983
  • 19
  • 69
  • 116
  • *"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. – Tomalak Nov 04 '16 at 13:53
  • @Tomalak - typo, my mistake. fixed it. – I wrestled a bear once. Nov 04 '16 at 13:55
  • 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. – Tomalak Nov 04 '16 at 13:58
  • 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. – I wrestled a bear once. Nov 04 '16 at 14:06
  • 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.) – Tomalak Nov 04 '16 at 14:11
  • "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. – I wrestled a bear once. Nov 04 '16 at 14:20