Saturday, February 19, 2011

How can I access element attributes from an IXMLDOMNode?

Hi all,

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.

From stackoverflow
  • 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.

    Symmetric : Thanks for the suggestion; I looked into QueryInterface, but couldn't get it to work. I've added my latest attempt to the original question; can you see anything wrong with what I'm doing? Thanks
    Symmetric : You've pointed me to the source of my problem; the QueryInterface call was correct, but my pParentNode was null, as the selectSingleNode call failed. The pParentElement(pParentNode) call is a much nicer way of performing the downcast though.
  • 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++ :(

    Symmetric : It turns out that my problem was not the question that I asked ;) What would I gain by using ATL here? (I'm not being difficult, I just don't know about ATL). The syntax looks pretty similar.

0 comments:

Post a Comment