Introduction
That's somewhat weird, isn't it? I mean, the whole thing with XML - a fancy combination of angle brackets and plain vanilla text has literally changed the IT world. There has been lots of rumors about XML, but what I think that it is a worthwhile technology - especially, when combined with immense power of XSLT. However, this is just an introductory thing. And here's the main idea.
Programmers used to use System Registry to keep program settings and stuff like that. Ah, I forgot. At the beginning there were INI files - a nice thing, but there were no INI Parsers (as opposed to variety of XML Parsers available now), there was no possibility of validation and transformation, plus they were not hierarchal. On the whole, they were fine, no more. Afterwards programs started to exploit System Registry - which had some kind of "built-in" validation and hierarchy support. But still that was not enough. First, there was no easy way for inexperienced users to export program settings (unless this feature was provided by the program itself), which is bad due to periodical system reinstalls. Next, Registry was always considered to be quite a beast. It can always become corrupt, it is always cluttered, it is bad. So, the only thing left is, not surprisingly, XML.
Background
In the beginning of my XML era, I was using explicit code to read attributes, write attributes, validate, whatever. It was quite a mess, so it was quite natural to write a set of classes which would handle all the routine tasks for me. No sooner said than done.
Internals
The library is pretty straightforward. It uses a combination of Microsoft XML Parser, STL, COM support classes and MFC, which, I think is not that good, but it's fine for the beginning. The code itself is around 200 lines long, so I'd better quote it over here.
namespace Xml
{
typedef std::map<CSTRING, _variant_t> CONTAINER_MVARIANT;
class XmlSettingsElement
{
CString m_strName;
CONTAINER_MVARIANT m_mvItems;
public:
XmlSettingsElement(void)
{
}
XmlSettingsElement(const CString &strName) :
m_strName(strName)
{
}
~XmlSettingsElement(void)
{
}
_variant_t& operator [] (const CString &strItem)
{ return m_mvItems[strItem]; }
void SetName(const CString &strName)
{ m_strName = strName; }
CString GetName(void) const
{ return m_strName; }
MSXML2::IXMLDOMElementPtr
Save(const MSXML2::IXMLDOMDocument2Ptr &xmlDoc) const
{
MSXML2::IXMLDOMElementPtr xmlElement
= xmlDoc->createElement((LPCTSTR)m_strName);
for(CONTAINER_MVARIANT::const_iterator mxvci = m_mvItems.begin();
mxvci != m_mvItems.end(); ++mxvci)
{
xmlElement->setAttribute(_bstr_t(mxvci->first), mxvci->second);
}
return xmlElement;
}
void Load(const MSXML2::IXMLDOMElementPtr& xmlElement)
{
for(long lItem = 0; lItem < xmlElement->attributes->length;
++lItem)
{
m_mvItems.insert(std::make_pair(
(LPCTSTR)xmlElement->attributes->item[lItem]->nodeName,
xmlElement->attributes->item[lItem]->nodeValue));
}
}
};
typedef std::map<CString, XmlSettingsElement>
CONTAINER_MXMLSETTINGSELEMENT;
class XmlSettings
{
CONTAINER_MXMLSETTINGSELEMENT m_mxseElements;
public:
XmlSettings()
{
}
~XmlSettings()
{
}
XmlSettingsElement& operator [] (const CString &strElement)
{
m_mxseElements[strElement].SetName(strElement);
return m_mxseElements[strElement];
}
MSXML2::IXMLDOMElementPtr Save(
const MSXML2::IXMLDOMDocument2Ptr &xmlDoc,
const CString& strName)
{
MSXML2::IXMLDOMElementPtr xmlElement
= xmlDoc->createElement((LPCTSTR)strName);
for(CONTAINER_MXMLSETTINGSELEMENT::const_iterator mxseci =
m_mxseElements.begin();
mxseci != m_mxseElements.end(); ++mxseci)
{
xmlElement->appendChild(mxseci->second.Save(xmlDoc));
}
return xmlElement;
}
void Save(const CString& strElementName, const CString& strFileName)
{
MSXML2::IXMLDOMDocument2Ptr xmlDoc(__uuidof(MSXML2::DOMDocument30));
xmlDoc->appendChild(Save(xmlDoc, strElementName));
xmlDoc->save((LPCTSTR)strFileName);
}
void Load(const MSXML2::IXMLDOMElementPtr& xmlElement)
{
for(long lItem = 0; lItem < xmlElement->childNodes->length;
++lItem)
{
MSXML2::IXMLDOMElementPtr xmlItem
= xmlElement->childNodes->item[lItem];
XmlSettingsElement xse(_variant_t(xmlItem->nodeName));
xse.Load(xmlItem);
m_mxseElements[_variant_t(xmlItem->nodeName)] = xse;
}
}
void Load(const CString& strFileName)
{
MSXML2::IXMLDOMDocument2Ptr xmlDoc(__uuidof(MSXML2::DOMDocument30));
if(VARIANT_FALSE == xmlDoc->load((LPCTSTR)strFileName))
return;
Load(xmlDoc->documentElement);
}
};
}
Using the Code
It is incredibly simple to use. You load file, you use settings, you save file. That's it.
This is how we load:
Xml::XmlSettings xsSettings;
xsSettings.Load(CString("Settings.xml"));
And that's it. It won't throw any exceptions if file is not loaded - it will simply initialize all internal data structures to empty ones. Note that std::map
is used throughout the class - it has lots of implications, but more on this later. Now, it's time to load settings.
int integerValue = xsSettings["runtimeSettings"]["integerValue"];
double doubleValue = xsSettings["runtimeSettings"]["doubleValue"];
CString stringValue = xsSettings["server"]["stringValue"];
xsSettings["runtimeSettings"]["integerValue"] = 123;
xsSettings["runtimeSettings"]["doubleValue"] = 123.355;
xsSettings["server"]["stringValue"] = _bstr_t("http://www.codeproject.com");
Now I have to note a simple fact. The indexing operator (operator []
in C++ terms) for std::map
first checks if there is such element in the map. If there is, it returns it. If not, first it creates it using default constructor (the default constructor for _variant_t
, thankfully, initializes all fields to zeros) and returns this newly created object. So if, for example, neither the Settings Element, nor the Attribute exist, it will create those two objects and return a reference to an empty _variant_t
. That's it.
And the final step. Saving the XML file.
xsSettings.Save(_T("settings"), CString("Settings.xml"));
All changes made to the internal map are saved to the XML file.
Points of Interest
As I already said, I use several different classes throughout the classes, so it would be my number one issue. And if you have any suggestions or something - drop them here. Thanks in advance.
History
- February 5, 2005. Version 1.0.
Initial release.