65.9K
CodeProject is changing. Read more.
Home

RTSXML Parser

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.67/5 (3 votes)

Mar 1, 2004

2 min read

viewsIcon

29961

downloadIcon

359

Extension of Object Oriented XML Parser.

Introduction

I have a project to develop flexible menus and screens, and I am thinking to use XML as my format for the configuration file.

Thanks to Mike Melnikov. I read his project: "object-oriented XML parser" and I thought about how to adopt the project to mine.

Unfortunately, I have difficulty to adopt his project, because:

  • I couldn’t put his project to became one .dll library and used by another .dll library.
  • I can’t find a way to get data without ‘breaking’ the object-oriented structure.
  • I like to use classes not structures.

So, I tried to read how his project worked and reengineered his project to my XML project. I changed the name to become RTSXML.dll.

Requirement

  • I need accessing tree in a comfort way (well, at least for myself :-)).
  • I need accessing child node in an elegant way, with no ‘breaking’ the OO concept.

Solution

I am still using the parser from ‘Object-oriented XML parser’ project and developed my own tree class structured.

// read xml document and parse info CRTSXMLNode tree 
CRTSXMLNode *CRTSXMLManager::Read(LPCTSTR filename)
{
    CRTSXMLNode *pRoot = NULL; 
    if (SUCCEEDED(CreateDocument()))  // create document
    {
        if (SUCCEEDED(m_pIXMLDOMDocument->put_async(VARIANT_FALSE)))
        {
            // load and parse
            VARIANT_BOOL vSuccess = 
                m_pIXMLDOMDocument->load(_variant_t(filename)); 
            if(vSuccess)
            {
                MSXML::IXMLDOMNodePtr pNode  = m_pIXMLDOMDocument;
                if (pNode)
                {
                    pRoot = new CRTSXMLNode;
                    pRoot->m_sTitle = "Root RTSXMLDocument";
                    pRoot->m_nOrder = 1; 
                    // create a tree of xml document
                    CreateRTSXMLNode(pNode, pRoot);
                    m_pIXMLDOMDocument = NULL; 
                    return pRoot;   
                    // success
                }
            }
        }
    } 

    // error found
    ReportError(m_pIXMLDOMDocument->GetparseError());
    return NULL;
}

The concept of tree structure is like this: in every node of the tree there are some child nodes and some attribute values:

class CRTSXMLNode  

{
public:
     [..deleted…]
     CMapStringToString m_pAttrs;
     CArray<CRTSXMLNode *, CRTSXMLNode *> m_pChilds;
     [...deleted…] 
public:
     CRTSXMLNode();
     virtual ~CRTSXMLNode(); 
public:
     int GetFirstAttr(CString &key, CString &value);
     int GetNextAttr(CString &key, CString &value);
     int GetAttr(LPCTSTR key, int &value, int failedValue=-1);
     int GetAttr(LPCTSTR key, CString &value);
     int NumberOfAttrs() { return m_pAttrs.GetCount(); } 
public:
     CRTSXMLNode *GetFirstChild();
     CRTSXMLNode *GetNextChild();
     CRTSXMLNode *GetChilds(LPCTSTR key);
     CRTSXMLNode *GetChild(int nOrder);
     int NumberOfChilds() { return m_pChilds.GetUpperBound()+1; }
     CRTSXMLNode *FindChilds(LPCTSTR keyTitle); 
public:
     void AddAttr(LPCTSTR key, LPCTSTR value) { m_pAttrs.SetAt(key, value); }
     void AddChild(CRTSXMLNode *node); 

     void ReleaseAttrs();
     void ReleaseChilds(); 
public:
     void Trace(int nLevel=0); 
}

I am using CMapStringToString to keep my attribute values, because I need a fast access to the attribute without worrying about the order of attribute appearance and using CArray to keep child nodes based on order of appearance.

How to use

I am thinking “reusability” without changing anything in future projects, so I created RTSXML.dll library for those implementations.

For using the library, I created two new classes outside of the library: CXMLWindowReader and CXMLWindow.

CXMLWindowReader is a class for preparing and reading the XML file. Result of this class is CXMLNode and becomes input for CXMLWindow.

CXMLWindow is a generalization of lexical class. For this sample, I created CXMLMenu which inherited from CXMLWindow.

class EXT_WINDOW_CLASS CXMLMenu : public CXMLWindow  
{
protected:
     CMenu *m_pMenu;
     CMenu m_pBuffer[100];
     int m_nBuffer; 
protected:
     int CreateMenu(CMenu *pMenu, CRTSXMLNode *pNode); 
public:
     CXMLMenu(CMenu *pMenu = NULL);
     virtual ~CXMLMenu(); 

public:
     void SetMenu(CMenu *pMenu) { m_pMenu = pMenu; } 
public:
     int Build(CRTSXMLNode *pNode); 
};

and the important thing is implementation of int Build(CRTSXMLNode *pNode) function, which looks like this:

int CXMLMenu::Build(CRTSXMLNode *pNode)
{
     return CreateMenu(m_pMenu, pNode);
} 
int CXMLMenu::CreateMenu(CMenu *pMenu, CRTSXMLNode *pNode)
{
     CString szValue;
     CString szDesc;
     int nId; 
     CRTSXMLNode *child = pNode->GetFirstChild();
     while (child)
     {
          if (child->m_sTitle == "popup")
          {
               CMenu *pSubMenu = &m_pBuffer[m_nBuffer++]; 
               pSubMenu->CreatePopupMenu();
               child->GetAttr("menu", szValue);
               pMenu->AppendMenu(MF_POPUP, (UINT)pSubMenu->m_hMenu, szValue);
               CreateMenu(pSubMenu, child);
          }
          else
          if (child->m_sTitle == "item")
          {
               child->GetAttr("menu", szValue);
               child->GetAttr("text", szDesc);
               child->GetAttr("id", nId); 
               if (szValue == "separator")
                    pMenu->AppendMenu(MF_SEPARATOR, 0);
               else
               {
                    pMenu->AppendMenu(MF_STRING, nId, szValue);
               }
          }

          child = pNode->GetNextChild();
     }
     return 1;
}

In “menu” node (not shown in code above. See the source code) there are two kinds of child nodes: they are “popup” and “item”.

“popup” is used to define popup menu and “item” is used to define item of the menu. Every popup menu can have two child nodes: these are another popup menu and item.

For complete reference, you could download my source code. Thank you.

In my next article, I will explain how to implement frame window using XML configuration file.