martin martin - 10 days ago 5
C++ Question

MSXML C++ Declare default namespace

I'm using Visual Studio C++ with MSXML imported (#import "msxml6.dll") to create xml documents using the smart pointers.

I use the setProperty() function to create the namespaces and I then add the corresponding attributes to the document element but when I try to declare a default namespace all of the elements below the document element have the attribute

xmlns=""
added to them.

Here is my code:

// Macro to check HRESULT
#define CheckHr(myHr) do{ hr = myHr; if(FAILED(hr)) throw _com_error(hr); }while(0)

void makeMyXml()
{
HRESULT hr{ S_OK };
MSXML2::IXMLDOMDocument3Ptr xDoc{ NULL };

try
{
// Create document
CheckHr(xDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60)));

// Add namespaces
CheckHr(xDoc->setProperty(L"SelectionNamespaces", _T("xmlns=\"http://myDefaultNamespaceURL\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"")));

// Add document element
CheckHr(xDoc->appendChild(xDoc->createElement(_T("root"))));

// Add namespace attributes to root
CheckHr(xDoc->GetdocumentElement()->setAttribute(_T("xmlns"), _T("http://myDefaultNamespaceURL")));
CheckHr(xDoc->GetdocumentElement()->setAttribute(_T("xmlns:xsi"), _T("http://www.w3.org/2001/XMLSchema-instance")));
CheckHr(xDoc->GetdocumentElement()->setAttribute(_T("xsi:schemaLocation"), _T("http://schemaLocationValue")));

CheckHr(xDoc->GetdocumentElement()->appendChild(xDoc->createElement(_T("exampleElement"))));

CheckHr(xDoc->save("test.xml"));

}
catch (_com_error &e)
{
// handle any thrown com errors here
}

return;
}


The xml this creates looks like this:

<root xmlns="http://myDefaultNamespaceURL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemaLocationValue">
<exampleElement xmlns=""/>
</root>


I haven't been able to find a way to just have
<exampleElement/>
instead of
<exampleElement xmlns=""/>

Answer

With MSXML, to create elements or attributes in a namespace, you need to use the createNode method https://msdn.microsoft.com/en-us/library/ms757901(v=vs.85).aspx where you use e.g. xDoc->createNode(1, "root", "http://myDefaultNamespaceURL") to create an element in the namespace http://myDefaultNamespaceURL. Make sure you use the same for all descendant elements you want to put in the same namespace. You can also use createNode to create attributes in a namespace e.g. createNode->(2, "xsi:schemaLocation", "http://www.w3.org/2001/XMLSchema-instance") and then add that to the attributes of the element.

The W3C DOM in level 2 and 3 has namespace aware createElementNS and setAttributeNS to be used in case of XML with namespaces but the MSXML API dates from before those levels and was never updated to match the W3C DOM, its only namespace aware method is createNode. The methods createElement and setAttribute are basically only useful to create XML without namespaces.

See also http://blogs.msmvps.com/martin-honnen/2009/04/14/creating-xml-with-namespaces-with-javascript-and-msxml/ which uses JScript with MSXML but obviously the problem and solution relating to proper API use is the same.

Comments