xmlbuilder2 can convert JS objects into XML nodes. Each key/value pair of a JS object is converted into an element node, where object key becomes the node name and object value becomes the node contents.

const { create } = require('xmlbuilder');

const obj = {
  root: 'text'
};
const doc = create().ele(obj);
console.log(doc.end({ prettyPrint: true }));
<?xml version="1.0"?>
<root>text</root>

If the object value is another JS object, it will be recursively converted into nodes.

const { create } = require('xmlbuilder');

const obj = {
  root: {
    foo: {
      bar: 'foobar'
    }
  }
};
const doc = create().ele(obj);
console.log(doc.end({ prettyPrint: true }));
<?xml version="1.0"?>
<root>
  <foo>
    <bar>foobar</bar>
  </foo>
</root>

If the object value is an array, each array item will be converted into a child node. In that case, name of the child nodes will be the object key.

const { create } = require('xmlbuilder');

const obj = {
  root: {
    child: [
      'one',
      'two',
      'three'
    ]
  }
};
const doc = create().ele(obj);
console.log(doc.end({ prettyPrint: true }));
<?xml version="1.0"?>
<root>
  <child>one</child>
  <child>two</child>
  <child>three</child>
</root>

Although this allows element and text nodes to be easily created, a special syntax is needed for other types of nodes and element attributes. This special syntax is provided with converter strings. Default converter strings and their behavior are as follows.

Converter Strings

Converter strings can be customized with the convert option while initializing the builder; see builder options.

att

Converts its key-value pair to an element attribute. Defaults to '@'.

obj = { pilot: { '@callsign': 'Maverick', '@rank': 'Lieutenant' } }

will be converted into:

<pilot callsign="Maverick" rank="Lieutenant"/>

ins

Converts its value to a processing instruction node. Defaults to '?'. Instruction target and value should be separated with a single space character.

obj = { 
  '?': 'background classified ref="NAM#123456"',
  pilot: 'Pete Mitchell'
}

becomes:

<?background classified ref="NAM#123456"?>
<pilot>Pete Mitchell</pilot>

text

Converts its value to a text node if it is a string, otherwise expands its value under its parent element node. Defaults to '#'.

obj = { monologue: {
  '#': 'Talk to me Goose!'
} }

becomes:

<monologue>Talk to me Goose!</monologue>

cdata

Converts its value to a CDATA section node. Defaults to '$'.

obj = { 
  '$': '<a href="https://topgun.fandom.com/wiki/MiG-28"/>',
  aircraft: 'MiG-28'
}

becomes:

<![CDATA[<a href="https://topgun.fandom.com/wiki/MiG-28"/>]]>
<aircraft>MiG-28</aircraft>

comment

Converts its value to a comment node. Defaults to '!'.

obj = {
  '!': 'Fictional; MiGs use odd numbers for fighters.',
  aircraft: 'MiG-28'
}

becomes:

<!--Fictional; MiGs use odd numbers for fighters.-->
<aircraft>MiG-28</aircraft>

Namespaces

When converting JS objects, element and attribute namespaces can be specified as follows:

obj = {
  'root@http://example.com/ns1': 'foo'
}

becomes:

<root xmlns="http://example.com/ns1">foo</root>