RTSXML Parser






2.67/5 (3 votes)
Mar 1, 2004
2 min read

29961

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.