Search code examples
c++xpathxercesxalan

XPath query returns a null node in a legacy C++ system using Xerces-C++ and Xalan-C++


I have been maintaining a legacy system that uses Xerces-C++ for XML parsing. I have to use Xalan-C++ to achieve full XPath support. I am using Xerces-C++ 3.2.3 and Xalan-C++ 1.12.0 - these versions are compatible with each other

I have been having issues with some code that I wrote based on this example guide: http://www.xatlantis.ch/index.php/blog/19-xml/26-x-path-with-xerces-and-xalan

I have checked other examples, but the above example is the only one I found that uses the XercesDOMParser wrapper. Other examples (including the one provided by Xalan-C++) use Xalan's own DOM implementation. I could technically use that as a very last resort, but I would very much prefer to use the Xerces DOM wrapper.

Here is a snippet of my code:

std::string xpath = "/some/path/with/namespaces";
const xalanc::XalanDOMString strExpression(xpath.c_str());
xercesc::DOMDocument* document = xml.parser().get().getDocument();
xercesc::DOMNode* root = document->getDocumentElement();

xalanc::XercesParserLiaison parserLiaison;
parserLiaison.setBuildWrapperNodes(true);
parserLiaison.setBuildMaps(true);

xalanc::XalanDocument* xalanDocument = parserLiaison.createDocument(document, false, false);
xalanc::XalanElement* documentElement = xalanDocument->getDocumentElement();
xalanc::XercesDocumentWrapper* xercesDocumentWrapper = parserLiaison.mapDocumentToWrapper(xalanDocument);
xalanc::XalanNode* rootContextNode = xercesDocumentWrapper->mapNode(root);
xalanc::XercesDOMSupport domSupport(parserLiaison);

if(rootContextNode != NULL) {
  xalanc::XPathEvaluator evaluator;
  xalanc::XalanNode* foundXalanNode = evaluator.selectSingleNode(
      domSupport,
      rootContextNode,
      strExpression.c_str(),
      documentElement);

  xercesc::DOMNode* targetNode = NULL;

  if(foundXalanNode != NULL) {
    xercesc::DOMNode const* constTargetNode = xercesDocumentWrapper->mapNode(foundXalanNode);
    targetNode = const_cast<xercesc::DOMNode*>(constTargetNode);
    std::cout << "value: " << targetNode->getNodeValue() << std::endl;
  }
}

This is the smallest snippet of code I could write to reproduce my problem, I apologise if it feels too big. Let me know if you need clarification on any part of it.

This code compiles successfully, but it does not output anything and it should output the value of an input element. Under further investigation, it seems that foundXalanNode is NULL, which tells me that the code is unable to find a node that matches my xpath. I tested the xpath expression I am using in an online browser, and it is correct. I also tested different variations of the xpath without namespaces.

Edit: As requested, here is an example XML:

<?xml version="1.0" ?>
<ns3:header created="2022-04-07T14:19:55.611+01:00" schemaVersion="1.0.0" xmlns="example.org" xmlns:ns2="example.org" xmlns:ns3="example.org">
  <ns3:point schemaVersion="1.1.0">
    <ns2:type>PRODUCTION</ns2:type>
  </ns3:point>
</ns3:header>

and here is the XPath I have been testing it with:

/ns3:header/ns3:point/ns2:type

Solution

  • I have tried to adapt the sample I mentioned in the comment to incorporate a Xerces DOM document and have Xalan wrap it; then I have run it against your sample XML with the arguments sample.xml / /ns3:header/ns3:point/ns2:type and the output is e.g. content: PRODUCTION, meaning the node is found.

    The code is

    #include <xalanc/Include/PlatformDefinitions.hpp>
    
    
    
    #include <cassert>
    
    
    #include <iostream>
    
    
    
    #include <xercesc/util/PlatformUtils.hpp>
    #include <xercesc/framework/LocalFileInputSource.hpp>
    
    
    
    #include <xalanc/PlatformSupport/XSLException.hpp>
    
    
    
    #include <xalanc/DOMSupport/XalanDocumentPrefixResolver.hpp>
    
    
    
    #include <xalanc/XPath/XObject.hpp>
    #include <xalanc/XPath/XPathEvaluator.hpp>
    
    
    
    #include <xalanc/XalanSourceTree/XalanSourceTreeDOMSupport.hpp>
    #include <xalanc/XalanSourceTree/XalanSourceTreeInit.hpp>
    #include <xalanc/XalanSourceTree/XalanSourceTreeParserLiaison.hpp>
    //#include <parser.h>
    
    
    #include <xercesc/dom/DOM.hpp>
    
    #include <xercesc/parsers/XercesDOMParser.hpp>
    
    #include <xalanc/XercesParserLiaison/XercesDocumentWrapper.hpp>
    
    using namespace XERCES_CPP_NAMESPACE;
    
    
    int
    main(
                int     argc,
                char*   argv[])
    {
        using std::cerr;
        using std::cout;
        using std::endl;
    
        int     theResult = 0;
    
        if (argc != 4)
        {
            cerr << "Usage: SimpleXPathAPI XMLFilePath Context XPathExpression" << endl;
    
            theResult = -1;
        }
        else
        {
            using xalanc::XSLException;
    
            try
            {
                using xercesc::XMLPlatformUtils;
    
                using xalanc::XPathEvaluator;
    
    
                XMLPlatformUtils::Initialize();
    
                XPathEvaluator::initialize();
    
                {
                    using xercesc::LocalFileInputSource;
    
                    using xalanc::XalanDocument;
                    using xalanc::XalanDocumentPrefixResolver;
                    using xalanc::XalanDOMString;
                    using xalanc::XalanNode;
                    //using xalanc::XalanSourceTreeInit;
                    //using xalanc::XalanSourceTreeDOMSupport;
                    //using xalanc::XalanSourceTreeParserLiaison;
                    using xalanc::XObjectPtr;
    
                    XercesDOMParser* parser = new XercesDOMParser;
                    parser->setDoNamespaces(true);
    
                    parser->parse(argv[1]);
    
                    DOMDocument* doc = parser->getDocument();
    
                    xalanc::XercesParserLiaison parserLiaison;
                    parserLiaison.setBuildWrapperNodes(true);
                    parserLiaison.setBuildMaps(true);
    
                    xalanc::XercesDOMSupport domSupport(parserLiaison);
    
                    // Initialize the XalanSourceTree subsystem...
                    //XalanSourceTreeInit     theSourceTreeInit;
    
                    // We'll use these to parse the XML file.
                    //XalanSourceTreeDOMSupport       theDOMSupport;
                    //XalanSourceTreeParserLiaison    theLiaison(theDOMSupport);
    
                    // Hook the two together...
                    //theDOMSupport.setParserLiaison(&theLiaison);
    
                    //const XalanDOMString    theFileName(argv[1]);
    
                    // Create an input source that represents a local file...
                    //const LocalFileInputSource  theInputSource(theFileName.c_str());
    
                    // Parse the document...
                    XalanDocument* const    theDocument = parserLiaison.createDocument(doc, false, false);
                            //theLiaison.parseXMLStream(theInputSource);
                    assert(theDocument != 0);
    
                    xalanc::XercesDocumentWrapper* xercesDocumentWrapper = parserLiaison.mapDocumentToWrapper(theDocument);
    
                    XalanDocumentPrefixResolver     thePrefixResolver(theDocument);
    
                    XPathEvaluator  theEvaluator;
    
                    // OK, let's find the context node...
                    XalanNode* const    theContextNode =
                            theEvaluator.selectSingleNode(
                                domSupport,
                                theDocument,
                                XalanDOMString(argv[2]).c_str(),
                                thePrefixResolver);
    
                    if (theContextNode == 0)
                    {
                        cerr << "Warning -- No nodes matched the location path \""
                             << argv[2]
                             << "\"."
                             << endl
                             << "Execution cannot continue..."
                             << endl
                             << endl;
                    }
                    else
                    {
                        xalanc::XPathEvaluator evaluator;
                        xalanc::XalanNode* foundXalanNode = theEvaluator.selectSingleNode(
                            domSupport,
                            theContextNode,
                            XalanDOMString(argv[3]).c_str(),
                            thePrefixResolver);
    
                        xercesc::DOMNode* targetNode = NULL;
    
                        if (foundXalanNode != NULL) {
                            xercesc::DOMNode const* constTargetNode = xercesDocumentWrapper->mapNode(foundXalanNode);
                            targetNode = const_cast<xercesc::DOMNode*>(constTargetNode);
                            std::cout << "content: " << XMLString::transcode(targetNode->getTextContent(), XMLPlatformUtils::fgMemoryManager) << std::endl;
                        }
     
                    }
                }
    
                XPathEvaluator::terminate();
    
                XMLPlatformUtils::Terminate();
            }
            catch(const XSLException&   theException)
            {
                using xalanc::XalanDOMString;
    
                XalanDOMString  theError;
    
                cerr << "XSL exception: "
                     << theException.defaultFormat(theError)
                     << endl;
    
                theResult = -1;
            }
            catch(...)
            {
                cerr << "Generic exception caught!" << endl;
    
                theResult = -1;
            }
        }
    
        return theResult;
    }
    

    I have used Xalan 1.12 and Xerces 3.2.3. No idea where your approach fails but hopefully the above helps you on your way to get your Xalan XPath evaluation against a Xerces DOM working.