Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

XML class used to store or load information

0.00/5 (No votes)
11 Feb 2003 1  
Serialize information stored as XML.

Introduction

This article describes a class used for reading and writing information stored as XML. The main task is to manage information, not to validate. There is no need for other components for using this class. It is very fast and this is also the main purpose for it, to read or write XML as fast as possible.

The XMLDOM component is harder to use and much slower. Sometimes you do not need to validate XML and the XML file is just plain XML and no formatting is done. Then I think you will like it.

In the downloadable file there is one help file (CHM) that describes methods. This file was done using doxygen. There are also small samples in the help file on how to use it.

The XML class is template based. Two methods need to be supported by the class that is wrapped, one for reading and one for writing. It is also possible to derive from the class and create your own reading or writing methods.

If you are reading UTF-8 formatted text, then you need to translate it first before sending it to the XML class. Unicode or ASCII is OK but other formats need to be converted before processing. If the class should have managed UTF-8 conversion or other types of features that isn�t really needed for streaming information to or from XML file, then this would have had impact on the speed.

It also uses buffered reading or writing. Blocks of information are completed in sections. I will try to update the code here with new features or add other types of code. But if you need the latest, then visit Gorep Development.

Note: The code included with this article is designed for effective use and not just meant for the tutorial.

Background

I created this class after having used XMLDOM. XML is a very helpful format when information should be stored. But if there is one "elephant" control like XMLDOM that is needed every time XML should be processed, then this might be little to much. Now it is a very simple task to use XML and the price is very small (tiny overhead).

Using the code

The class is really simple to use but the internals is advanced. It uses some general collection classes that are included. Also the CString class. Don�t know why the MFC team didn�t build the CString as one standalone class??? As time goes you will get a little tired including MFC, only because you need CString. I use WTL and ATL mostly, so the solution was to create my own CString. Also I like to do portable code ;-).

If you have any wishes about new or improved functionality, then don't hesitate to ask for it. I will try to add it (if it doesn�t interfere with speed).

Here is some sample code from the download demo:

// class used for reading and writing

class CXMLBuffer
{
public:
   CXMLBuffer() : m_uIndex(0) { }
   CXMLBuffer( CString string ) : m_stringXML( string ), m_uIndex(0) { }
   
   CString GetXML() {
      return m_stringXML; }

   void Write( const TCHAR* pszBuffer, UINT uCount, UINT* puCount )
   {
      m_uIndex += uCount;
      TCHAR* psz = m_stringXML.GetBuffer( m_uIndex + 1 );
      psz = &psz[m_uIndex - uCount];
      memcpy( psz, pszBuffer, uCount );
      psz[uCount] = '\0';
      m_stringXML.ReleaseBuffer();
   }

   UINT Read( TCHAR* pszBuffer, UINT uCount, UINT* puCount )
   {
      TCHAR* psz = m_stringXML.GetBuffer( 0 );
      if( uCount + m_uIndex > m_stringXML.GetLength() )
      {
         uCount = m_stringXML.GetLength() - m_uIndex;
      }
      memcpy( pszBuffer, &psz[m_uIndex], uCount );
      m_uIndex += uCount;
      if( puCount != NULL )
      {
         *puCount = uCount;
      }
      return uCount;
   }

   unsigned m_uIndex;
   CString m_stringXML;
};

using namespace gd_xml_stream;

/**
 * SAMPLE 1
 * Extract words from text and insert them to XML buffer
 */
void Cxml1Doc::OnSampleSurroundwords()
{
   CString string, stringWord;
   reinterpret_cast<CEditView*>
     (m_viewList.GetHead())->GetWindowText( string );

   if( string.IsEmpty() )
   {
      ::AfxMessageBox( _T("No text to surround, type som words"), MB_OK );
      return;
   } // if( string.IsEmpty() ){


   const TCHAR* pszPosition = string;
   CXMLBuffer Buffer;
   CXMLArchive<CXMLBuffer> ar( &Buffer, 
         CXMLArchive<CXMLBuffer>::eWrite );

   ar << Child << _T("DOCUMENT");
   while( *pszPosition )
   {
      if( *pszPosition <= ' ' )
      {
         if( stringWord.IsEmpty() == false )
         {
            ar << Child << _T("WORD") << 
                 attribute( _T("LENGTH"), stringWord.GetLength() ) 
                 << (const TCHAR*)stringWord << EndNode;
         }

         while( *pszPosition && *pszPosition <= ' ' ) pszPosition++;
         stringWord.Empty();
         continue;
      }

      stringWord += *pszPosition;
      pszPosition++;
   } // while( *pszPosition ){


   if( stringWord.IsEmpty() == false )
   {
      ar << Child << _T("WORD") << attribute( _T("LENGTH"), 
          stringWord.GetLength() ) 
          << (const TCHAR*)stringWord << EndNode;
   }

   ar << End;
   ar.Flush();

   reinterpret_cast<CEditView*>
     (m_viewList.GetHead())->SetWindowText( Buffer.GetXML() );
}

/**
 * SAMPLE 2
 * Add values to XML object
 */
void Cxml1Doc::OnSampleHardcoded1()
{
   CXMLBuffer Buffer;
   CXMLArchive<CXMLBuffer> ar( &Buffer, 
       CXMLArchive<CXMLBuffer>::eWrite );

   ar << Child << _T("DOCUMENT")
         << Child << _T("CHILD") 
         << attribute( _T("A1"), 1 ) 
         << attribute( _T("A2"), 1.1 ) 
         << attribute( _T("A3"), _T("TEXT") ) 
         << _T("Value") << EndNode
         << Child << _T("CHILD") 
         << attribute( _T("A1"), 1 ) 
         << attribute( _T("A2"), 1.2 ) 
         << attribute( _T("A3"), _T("TEXT") ) 
         << _T("Value")
         << Child << _T("CHILD") 
         << attribute( _T("A1"), 1 ) 
         << attribute( _T("A2"), 1.3 ) 
         << attribute( _T("A3"), _T("TEXT") ) 
         << _T("Value") << EndNode << EndNode;
   ar << End;
   reinterpret_cast<CEditView*>
      (m_viewList.GetHead())->SetWindowText( Buffer.GetXML() );
}

/**
 * SAMPLE 3
 * Show how it is possible to read from XML formated text.
 */
void Cxml1Doc::OnSampleReadtext()
{
   TCHAR* pszText =
      _T("<DOCUMENT>")
         _T("<ITEM A1=\"Attribute value\">Item value</ITEM>")
         _T("<ITEM A1=\"Attribute value\">Item value</ITEM>")
         _T("<ITEM A1=\"Attribute value\">")
            _T("<CHILDITEM A1=\"Attribute value\">xxxxxxxx</CHILDITEM>")
         _T("</ITEM>")
      _T("</DOCUMENT>");
   CString stringReport;
   CXMLBuffer Buffer( pszText );
   CXMLArchive<CXMLBuffer> ar( &Buffer );

   while( ar.ReadNext() == true )
   {
      if( ar.IsEndTag() == false )
      {
         CString stringName = (const TCHAR*)ar.GetName();

         stringReport += stringName;

         stringReport += _T("\r\n");

         if( stringName == _T("ITEM") )
         {
            ar.PreReadNext();
            CString stringNextName = (const TCHAR*)ar.GetNextName();
            if( stringNextName == _T("CHILDITEM") )
            {
               stringReport += _T("Next item is named CHILDITEM.");
               if( ar.HasNextChildren() == false )
               {
                  stringReport += 
                    _T(" And that item don't have any childrens");
               }
               stringReport += _T("\r\n");
            }
         }
      }
   }
   reinterpret_cast<CEditView*>
     (m_viewList.GetHead())->SetWindowText( stringReport );
}

History

Thanks to Stephan Rodriguez who pointed out compliance problem with VC 6.0. With help from him it now should work with VC 6.0 (don�t have VC 6.0 myself).

Added sample for saving file and class for converting to UTF8.

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