Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

XMLLib for PUGXML with XPath

, 29 Oct 2009 CPOL
A library for PugXML which implements XPath
// UnitTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "UnitTest.h"
#include "PugXMLNode.h"     // XML class

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

//
// Convert an XML element to a CString
// This is the only dependency on MFC
//
void XMLToCString(xml_node xml, CString& strCString)
{
    const char* str;
    std::ostringstream strm;
    std::string stl_string;
    strm << xml;
    stl_string = strm.str();
    str = stl_string.c_str();
    strCString = str;
}

//
// Display a node in a MessageBox
//
void DebugNode(xml_node& node, LPCTSTR szComment)
{
    CString str;
    XMLToCString(node, str);
    ::MessageBox(NULL, str, szComment, MB_OK);
}

//
// Test PugXML
//
void TestXML()
{
    xml_parser* xml = new xml_parser(); // Construct.
    xml->create();
    xml_node root = xml->document();
    xml_node node = root.append_child(node_element); // Add a child element.
    node.append_attribute("111", "222");
    node.name("original");
    DebugNode(node, "node");

    // Correct way to construct a new outside of a document and append it
    // No exceptions. No memory leaks.
    xml_node_struct* pNode = new_node();    // Allocates node the right way
    xml_node Node = xml_node(pNode);        // Wrap it in a xml_node class
    Node.name("allocNode");
    DebugNode(Node, "Node");
    root.append_child_noalloc(pNode);
    Node.append_attribute("attr1", "val1");
    Node.append_attribute("attr2", "val2");
    DebugNode(Node, "Node with attribute");

    // Modify first node after appending second node
    node.append_attribute("333", "444");

    DebugNode(root, "root");
    DebugNode(node, "original node with changes intact");
    xml->clear(); // Clear for the next test.
    delete xml;

    // Test creating and destroying a stand-alone node
    xml_node_struct* pNode1 = new_node();    // Allocates node the right way
    Node = xml_node(pNode1);                // Wrap it in a xml_node class
    Node.name("Node1");
    Node.attribute("one") = "two";
    DebugNode(Node, "Node 1");
    free_node_recursive(pNode1);
}

//
// Portion of test sample in PugXML
//
void TestSample()
{
    xml_parser* xml = new xml_parser(); // Construct.
    xml->create();
    xml_node root = xml->document();
    xml_node example = root.append_child(node_element); // Add a child element.
    example.name(_T("example")); // Give it a name.
    example.attribute(_T("is-simple")) = false; // Add some attributes.
    example.attribute(_T("example-id")) = _T("B");
    example.attribute(_T("i-am-a-goose")) = true;
    example.attribute(_T("type-of-goose")) = _T("Branta Canadensis Maxima");
    example.attribute(_T("goosely-wingspan")) = 6.54321;
    example.attribute(_T("goosely-wingspan-units")) = _T("US-Feet");
    xml_node comment = example.append_child(node_comment); // Add a comment.
    comment.value(_T("May I make a suggestion?"));
    xml_node data = example.append_child(node_pcdata); // Add some PCDATA.
    data.value(_T("Honk whenever you please."));
    xml_node thingy = example.append_child(node_element); // Add a sub-element.
    thingy.name(_T("a-goosely-sound"));
    thingy.append_child(node_pcdata).value(_T("Honketty-harrooo!"));

    DebugNode(root, "TestSample");
	xml->clear(); // Clear for the next test.
	delete xml;
}

//
// Build an XML document on the fly and perform an XPATH query on it
//
void TestXPath()
{
    xml_parser* xml = new xml_parser(); // Construct.
    xml->create();
    xml_node root = xml->document();
    CPugXMLNode Node = root.append_child(node_element);

    Node.name("allocNode");
    Node.append_attribute("attr1", "val1");
    Node.append_attribute("attr2", "val2");

    CPugXMLNode sub1 = Node.append_child(node_element);
    sub1.name("sub1");
    CPugXMLNode sub2 = Node.append_child(node_element);
    sub2.name("sub2");
    CPugXMLNode sub3 = Node.append_child(node_element);
    sub3.name("sub3");

    // Display what we have
    DebugNode(Node, "Node with attribute");

    // XPath queries
    Array<xml_node_struct*> nodes = Node.FindNodes("//sub2 | //sub3");
    for (int i = 0; i < nodes.getSize(); i++)
    {
        CString strMsg;
        strMsg.Format("Node %d", i);
        DebugNode(xml_node(nodes[i]), strMsg);
    }

	xml->clear(); // Clear for the next test.
	delete xml;
}

//
// See if user wants to continue with this test
//
bool ReqContinue(TCHAR* szPath, TCHAR* szExpr, int nExpected, int nFound)
{
    if (nFound == 1 && nExpected == 1)
        return true;

    CString strMsg;
    if (nFound == 0)
    {
        strMsg.Format("%s %s: Found nothing.", szPath, szExpr);
        ::AfxMessageBox(strMsg);
        return false;
    }

    if (nFound != nExpected)
    {
        strMsg.Format("%s %s: Expected: %d Found: %d\nShow Results?", szPath, szExpr, nExpected, nFound);
        int nRet = ::AfxMessageBox(strMsg, MB_YESNO);
        return nRet == IDYES;
    }
    else
    {
        strMsg.Format("%s %s: Found: %d\nShow Results?", szPath, szExpr, nFound);
        int nRet = ::AfxMessageBox(strMsg, MB_YESNO);
        return nRet == IDYES;
    }

    return false;
}

//
// Read an XML file, parse it, and perform an XPATH query
//
void TestXPathFile(TCHAR* szPath, TCHAR* szExpr, int nExpected)
{
    xml_parser* xml = new xml_parser(); // Construct.
    if (!xml->parse_file(szPath))
    {
        ::AfxMessageBox("Parse failed");
    }

    // If parse only, exit
    if (szExpr == NULL)
        return;

    CPugXMLNode root = xml->document();

    // XPath queries
    try
    {
        Array<xml_node_struct*> nodes = root.FindNodes(szExpr);
        if (ReqContinue(szPath, szExpr, nExpected, nodes.getSize()))
        {
            for (int i = 0; i < nodes.getSize(); i++)
            {
                CString strMsg;
                strMsg.Format("%s %s: %d", szPath, szExpr, i);
                DebugNode(xml_node(nodes[i]), strMsg);
            }
        }
    }
    catch(BString s)
    {
        ::AfxMessageBox(s.getAsCStr());
    }
    catch(...)
    {
        ::AfxMessageBox("xpath failed");
    }

    // This takes care of memory leaks
    xml->clear();
    delete xml;
}


//
// Test FindValues() function
//
void TestFindValues(char* szPath)
{
    char szExpr[] = "//param/value/struct/member/name";

    xml_parser* xml = new xml_parser(); // Construct.
    if (!xml->parse_file(szPath))
    {
        ::AfxMessageBox("Parse failed");
    }

    // If parse only, exit
    if (szExpr == NULL)
        return;

    CPugXMLNode root = xml->document();

    // XPath queries
    try
    {
        // Get one value into a BString
        BString value = root.FindValue(szExpr);
        ::AfxMessageBox(value.getAsCStr());

        // Get a bunch of values into an Array<BString>
        Array<BString> values = root.FindValues(szExpr);
        for (int i = 0; i < values.getSize(); i++)
        {
            CString strMsg;
            strMsg.Format("Value %d: %s", i, values[i].getAsCStr());
            ::AfxMessageBox(strMsg);
        }
    }
    catch(BString s)
    {
        ::AfxMessageBox(s.getAsCStr());
    }
    catch(...)
    {
        ::AfxMessageBox("xpath failed");
    }

    // This takes care of memory leaks
    xml->clear();
    delete xml;
}

//
// Supply test information from the command line
//
void CommandLineTests(int argc, TCHAR* argv[])
{
    // Command-line tests
    if (argc == 3)
    {
        TestXPathFile(argv[1], argv[2], 0);
    }
    else if (argc == 2)
    {
        TestXPathFile(argv[1], NULL, 0);
    }
    else
    {
        CString strMsg;
        strMsg.Format("Usage: %s <xmlfilename> [<searchexpression[",
            argv[0]);
        ::AfxMessageBox(strMsg);
    }
}

//
// Mainline test driver
//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        cerr << _T("Fatal Error: MFC initialization failed") << endl;
        nRetCode = 1;
    }
    else
    {
        // Simple sanity tests
        TestSample();
        TestXML();
        TestXPath();
        TestFindValues("TestData\\response.xml");

        // Test using external files
        //CommandLineTests(argc, argv);

        // Test exception handling - invalid search string
        TestXPathFile("TestData\\response.xml", "methodResponse", 0);

        // Example 1 Select the root element
        TestXPathFile("TestData\\hamlet.xml", "/PLAY", 1);

        // Example 2 Select all elements
        TestXPathFile("TestData\\hamlet.xml", "//LINE", 59);

        // Example 4 Expression in square brackets to further specify
        // an element /AAA/BBB[1] /AAA/BBB[last()]
        TestXPathFile("TestData\\sample1.xml", "//MODULE[1]", 1);
        TestXPathFile("TestData\\sample1.xml", "//MODULE[last()]", 1);

        // Select all elements with a given attribute
        TestXPathFile("TestData\\Capture_2003_05_27_15_01_07.xml", "//window[@class='Button']", 3);

        // Select all elements with a given attribute and a given value
        TestXPathFile("TestData\\cathi.xml", "//item[@key='Version'", 2);

        //Example 8 Function name()
        TestXPathFile("TestData\\sample1.xml", "//*[name()='MODIFIED``']", 13);

        // Get the member with the name 'Humor'. Get name and value nodes
        TestXPathFile("TestData\\response.xml", "//member/*[text()='Humor']/parent::*", 1);

        // Get the member with the name 'Humor'. Return just the value node
        // (The following two expressions are equivalent:
        TestXPathFile("TestData\\response.xml", "//member/*[text()='Humor']/parent::*/child::value", 1);
        TestXPathFile("TestData\\response.xml", "//member/*[text()='Humor']/parent::*/value", 1);

        // Items from a collection
        TestXPathFile("TestData\\rss.xml", "//item[1]", 1);
        TestXPathFile("TestData\\rss.xml", "//item[last()]", 1);
        TestXPathFile("TestData\\rss.xml", "//item[last()]/link", 1);
        TestXPathFile("TestData\\rss.xml", "//skipHours/hour[1]", 1);
        TestXPathFile("TestData\\rss.xml", "//skipHours/hour[last()]", 1);

        // Another test
        TestXPathFile("TestData\\responseManila.xml", "//member/*[text()='msgnum']/parent::*/value", 1);
    }

    return nRetCode;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

JCrane2
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 29 Oct 2009
Article Copyright 2003 by JCrane2
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid