Add an attribute to an MSXML node

This question came up in the forums, and I thought it was worth blogging about because there are a number of considerations "off to the side".

The original question was whether it's possible to add an attribute to an IXMLDOMNode, without having to downcast to an IXMLDOMElement.

The straightforward answer is yes, you can:

function createDocumentFromText(xmlText, version) {
  var result = new ActiveXObject("Msxml2.DOMDocument." + version);
  result.async = false;
  result.loadXML(xmlText);
  return result;
}

var doc1 = createDocumentFromText("<book><text>hello</text></book>", "6.0");
var book1 = doc1.documentElement;

var attribute = doc1.createAttribute("attribute");
attribute.value = "value";
book1.attributes.setNamedItem(attribute);

WScript.Echo(doc1.xml);

It's kind of hard to see that we're not actually using IXMLDOMElement members, but you can see from the documentation that attributes is defined on the node interface, not the element interface. This produces this output:

<book attribute="value"><text>hello</text></book>

However, there are a couple of things to consider. First, if the node you're working is not an element, the attributes collection will be NULL, in which case our sample code would just fail. The documentation spells this out pretty clearly.

Second, elements aren't the only nodes that take attributes. For example, the XML declaration <?xml version="1.0" ... ?> also has attributes.

Finally, the setNamedItem method doesn't really append an attribute, but instead just sets the named value, which may turn out to be an append operation or a replace operation. So you can come along with a different attribute that has the same name, and the new attribute wins. Let's add this bit of code to our sample.

WScript.Echo("Overwriting with another attribute with same name");
var attribute2 = doc1.createAttribute("attribute");
attribute2.value = "value2";
book1.attributes.setNamedItem(attribute2);
WScript.Echo(doc1.xml);

And now the new output is this.

<book attribute="value"><text>hello</text></book>

Overwriting with another attribute with same name
<book attribute="value2"><text>hello</text></book>

Enjoy!