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 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;
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;
}
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++;
}
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() );
}
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() );
}
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.