How can I access element attributes from an IXMLDOMNode?

18.3k views Asked by At

I'm building an XML DOM document in C++. My problem is this: I execute an XPATH query from an Element in my Document, which I know will return another Element. The elementPtr->selectSingleNode call returns an IXMLDOMNode. How can I gain access to the attributes of this node?

Part of me wants to downcast the Node to an Element, but I couldn't get the cast to work.

I tried

MSXML2::IXMLDOMElementPtr pParentElement;
pParentNode->QueryInterface(__uuidof(MSXML2::IXMLDOMElement), 
                            (void**) &pParentElement);

Which results in the following runtime error:

0x0057cc58 _com_error::`scalar deleting destructor'(unsigned int)

The other route I tried was to just use nodes:

MSXML2::IXMLDOMNodePtr pParentNode = 
    pParameterElement->selectSingleNode("parent");
MSXML2::IXMLDOMNamedNodeMap* pParentAttributes;
pParentNode->get_attributes(&pParentAttributes);

MSXML2::IXMLDOMNodePtr pCategoryNameNode = 
    pParentAttributes->getNamedItem("Category");
VARIANT value;
pCategoryNameNode->get_nodeValue(&value);
CString categoryName = value;

This fails at "parentNode->get_attributes()".

It seems like I'm missing something; the API should not be this hard to use.

--edit--

What I was missing was that the selectSingleNode call was failing, leaving me with a NULL pointer. You can't call QueryInterface on that, neither can you call get_attributes on it :P

I've selected the answer that fits the question that I asked, not the answer that helped me to realise that I asked the wrong question.

3

There are 3 answers

3
Greg Domjan On BEST ANSWER

I don't see anything wrong with what you have written.

The smart com pointers will help you convert if they can, you don't have to write the query interface yourself.

MSXML2::IXMLDOMNodePtr pParentNode = pParameterElement->selectSingleNode("parent");
MSXML2::IXMLDOMElementPtr pParentElement( pParentNode );

Using the Ptr types is a bit painfull in my opinion, though the MSXML interface favours them. Here is an equivelant example using ATL

CComPtr<IXMLDOMNode> node = ...;
CComQIPtr<IXMLDOMElement> elementNode( node );

if( elementNode ) { 
// it was an element!
} else { 
// it's something else try again? 
}

The other attempt would look like...

CComPtr<IXMLDOMNamedNodeMap> attributes;
node->get_attributes( &attributes );
if( attributes ) {
  _bstr_t name( L"category" );
  attributes->getNamedItem(name);
}

And it's COM, it's always hard to use in C++ :(

0
xianzhi gao On

CComPtr is necessary for IXMLDOMNamedNodeMap, otherwise there would be a exception:

object of abstract class type IXMLDOMNamedNodeMap is not allowed

2
DavidK On

How did you try to do the downcast from IXMLDOMNode to IXMLDOMElement? You can't just use a C++ cast for that, as it's a COM object: you've got to use QueryInterface().


Looking at your QueryInterface() code, some thoughts:

  • Is pParentNode definitely not null? I don't think that this is the problem, given what you get, but it's worth checking.
  • The QueryInterface() call isn't quite right, I think: you've got to call AddRef() one way or another on the returned interface, and your code won't. As another poster noted, you can get _com_ptr_t<> to do this for you:

    MSXML2::IXMLDOMElementPtr pParentElement(pParentNode);
    

Doing this will, I hope, stop that "scalar deleting destructor" error that's probably caused by an AddRef()/Release() mis-match.

Anyway, try the above and see if pParentElement is null or not. If it is, the next thing I'd suggest is calling get_nodeType() on pParentNode to see what sort of node it really is. This might give a clue as to whether the XPath is not returning what you expect.