Click here to Skip to main content
Click here to Skip to main content

XML Settings

, 5 Feb 2005
Rate this:
Please Sign up or sign in to vote.
Using XML to store settings.

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
{    
    //
    // Variants map
    //
    typedef std::map<CSTRING, _variant_t> CONTAINER_MVARIANT;

    //
    // Represents a single Element in the XML Settings file
    //
    class XmlSettingsElement
    {
        // Element name
        CString m_strName;
        // Items container
        CONTAINER_MVARIANT m_mvItems;
    public:
        //
        // Default constructor        
        XmlSettingsElement(void)
        {
        }

        //
        // Constructor
        XmlSettingsElement(const CString &strName) :
            m_strName(strName)
        {
        }

        //
        // Destructor
        ~XmlSettingsElement(void)
        {
        }

        //
        // Indexing
        _variant_t& operator [] (const CString &strItem)
        { return m_mvItems[strItem]; }

        //
        // Sets name
        void SetName(const CString &strName)
        { m_strName = strName; }

        //
        // Gets name
        CString GetName(void) const
        { return m_strName;    }

        //
        // Saves Element contents
        MSXML2::IXMLDOMElementPtr 
                Save(const MSXML2::IXMLDOMDocument2Ptr &xmlDoc) const
        {
            MSXML2::IXMLDOMElementPtr xmlElement 
                                 = xmlDoc->createElement((LPCTSTR)m_strName);

            //
            // Adding attributes
            for(CONTAINER_MVARIANT::const_iterator mxvci = m_mvItems.begin();
                mxvci != m_mvItems.end(); ++mxvci)
            {
                xmlElement->setAttribute(_bstr_t(mxvci->first), mxvci->second);
            } // for

            return xmlElement;
        }

        //
        // Loading Element contents
        void Load(const MSXML2::IXMLDOMElementPtr& xmlElement)
        {
            //
            // Loading items
            //
            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));
            }
        }
    };


    //
    // XML Settings Elements map
    //
    typedef std::map<CString, XmlSettingsElement> 
                                             CONTAINER_MXMLSETTINGSELEMENT;

    //
    // XML Settings class
    //
    class XmlSettings
    {
        // Container
        CONTAINER_MXMLSETTINGSELEMENT m_mxseElements;
    public:
        XmlSettings()
        {
        }

        ~XmlSettings()
        {
        }

        //
        // Indexing
        XmlSettingsElement& operator [] (const CString &strElement)
        {
            //
            // Return existing one, or create a new element
            m_mxseElements[strElement].SetName(strElement);
            return m_mxseElements[strElement];
        }

        //
        // Save contents
        MSXML2::IXMLDOMElementPtr Save(
                         const MSXML2::IXMLDOMDocument2Ptr &xmlDoc, 
                         const CString& strName)
        {
            MSXML2::IXMLDOMElementPtr xmlElement
                                   = xmlDoc->createElement((LPCTSTR)strName);

            //
            // Saving all Elements in the container
            for(CONTAINER_MXMLSETTINGSELEMENT::const_iterator mxseci = 
                                                      m_mxseElements.begin(); 
                mxseci != m_mxseElements.end(); ++mxseci)
            {
                xmlElement->appendChild(mxseci->second.Save(xmlDoc));
            } // for

            return xmlElement;
        }

        //
        // Save contents to the XML file
        void Save(const CString& strElementName, const CString& strFileName)
        {
            MSXML2::IXMLDOMDocument2Ptr xmlDoc(__uuidof(MSXML2::DOMDocument30));
            xmlDoc->appendChild(Save(xmlDoc, strElementName));

            xmlDoc->save((LPCTSTR)strFileName);
        } 

        //
        // Load contents
        void Load(const MSXML2::IXMLDOMElementPtr& xmlElement)
        {
            //
            // Loading elements
            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;
            } // for
        }

        //
        // Load contents from the XML file (from the document element)
        void Load(const CString& strFileName)
        {
            MSXML2::IXMLDOMDocument2Ptr xmlDoc(__uuidof(MSXML2::DOMDocument30));
            if(VARIANT_FALSE == xmlDoc->load((LPCTSTR)strFileName))
                return;

            Load(xmlDoc->documentElement);
        }
    };
} // namespace Xml

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:

//
// Should be member variable, I guess
Xml::XmlSettings xsSettings;

//
// Note the CString thing
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.

//
// Loading settings
int integerValue = xsSettings["runtimeSettings"]["integerValue"];
double doubleValue = xsSettings["runtimeSettings"]["doubleValue"];
CString stringValue = xsSettings["server"]["stringValue"];

//
// Saving settings
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

AntonGogolev
Web Developer
Russian Federation Russian Federation
I'll think about it later on...

Comments and Discussions

 
GeneralRuntime Error! - Sample would be fine PinmemberRjaReini23-Nov-07 6:08 
GeneralCompile error - VC++ 8 Pinmemberindyspike15-Jan-07 5:55 
GeneralFailure after Close Pinmember123qwertz@trash-mail.de19-Dec-06 21:29 

void Release() throw()
{
ATLASSERT( nRefs != 0 );
 
if( _InterlockedDecrement( &nRefs ) <= 0 )
00438A42 lea eax,[ecx+0Ch]
00438A45 or edx,0FFFFFFFFh
00438A48 lock xadd dword ptr [eax],edx /*<< HERE*/
00438A4C dec edx
00438A4D test edx,edx
00438A4F jg ATL::CStringData::Release+19h (438A5Bh)
{
pStringMgr->Free( this );
00438A51 mov eax,dword ptr [ecx]
00438A53 mov edx,dword ptr [eax]
00438A55 push ecx
00438A56 mov ecx,eax
00438A58 call dword ptr [edx+4]
}
}
00438A5B ret

The Parser Works fine, but if my app end a [debug][cancel][send to ms] Erro occurres. I just have to do:

CXMLSettings *xmlSettings;
xmlSettings = new CXMLSettings();

anywhere to cause this.
Generalgood work PinmemberHesham Desouky23-Sep-06 4:35 
GeneralProblem with [] PinmemberDevil for ever2-Feb-06 8:02 
GeneralRe: Problem with [] PinmemberDjinnKahn13-Jul-06 11:36 
GeneralLinking problem Pinmemberev226-Sep-05 20:56 
Generalmain Pinmembercbach24-Mar-05 21:25 
GeneralRe: main Pinmember123qwertz19-Dec-06 23:07 
GeneralMFC dependency PinmemberJimmyRopes12-Feb-05 16:57 
GeneralRe: MFC dependency PinmemberSl0n16-Feb-05 21:01 
GeneralCompile problem Pinmemberastromex7-Feb-05 11:04 
GeneralRe: Compile problem PinmemberSl0n8-Feb-05 4:42 
GeneralFile not found PinmemberPJ Arends5-Feb-05 11:05 
GeneralRe: File not found PinmemberSl0n6-Feb-05 22:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141030.1 | Last Updated 5 Feb 2005
Article Copyright 2005 by AntonGogolev
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid