CString XmlOut( LPCSTR sTag, LPCSTR sData )
{
CString sOut;
sOut.Format("<%s>%s</%s>\r\n", sTag, sData, sTag ); // tag twice!
return( sOut );
}
...and call it like:
m_sOut += XmlOut( "LastName", rc.sNameLast );
I actually ended up using C/C++'s variable argument list capability to write a general-purpose function that would accept a tag name, a tag value, and any number of attribute name/value pairs. The details of that are described here:
CString XmlElem( LPCSTR pszElemName, LPCSTR pszData, BOOL fLineBreak/*=FALSE*/, BOOL fNothingIfBlank/*=TRUE*/ )
{
CString sData= pszData;
CString sRet;
CString sEndTag= pszElemName;
int nOffset= sEndTag.Find(" " );
if ( nOffset != -1 ) sEndTag= sEndTag.Left( nOffset );
sData.TrimLeft( " \r\n");
sData.TrimRight(" \r\n");
if ( sData == "" ) {
if ( ! fNothingIfBlank ) {
sRet.Format("<%s></%s>", pszElemName, (LPCSTR)sEndTag );
}
}
else {
sRet.Format("<%s>%s</%s>", pszElemName, (LPCSTR)sData, (LPCSTR)sEndTag );
if ( fLineBreak ) {
sRet.Insert(0,"\r\n");
}
}
return sRet;
}
CString sXML=
XmlElem( "Name",
XmlElem( "FirstName", cr.m_sNameFirst )
+XmlElem( "MiddleName", cr.m_sNameMiddle )
+XmlElem( "LastName", cr.m_sNameLast )
+XmlElem( "Generation", cr.m_sNameSuffix )
);
...to output XML like:
CString sAllCustomers, sElemOneCustomer;
while ( ! cr.IsEOF() ) {
sElemOneCustomer=
GetElemCustName( cr ) // <Name>...</Name>
+GetElemCustAddr( cr ) // <Address>...</Address>
+GetElemCustPhone( cr ) // <Phone>...</Phone>
+XmlElem( "Rating", cr.m_sRating )
;
sAllCustomers += sElemOneCustomer;
cr.MoveNext();
}
CString sXML= XmlElem( "Customers", sAllCustomers );
// XmlGen.h
//
#pragma once
const int CNUM_nMaxLenTmpXML= 10000;
class CXmlGen
{
public:
CXmlGen( );
virtual ~CXmlGen();
CString GetXml();
void SetXml( LPCSTR szTxt );
void LineBreaks( BOOL fBreaks=TRUE ) {m_fAddLineBreaks= fBreaks;};
void SkipBlanks( BOOL fSkip=TRUE ) {m_fNothingIfBlank= fSkip; };
void XmlOutTag( LPCSTR szElem );
void XmlOutElem( LPCSTR sElem, LPCSTR sVal );
void XmlOutElem( LPCSTR sElem, int nVal );
void XmlOutAttr( LPCSTR sAttr, LPCSTR sVal );
void XmlOutAttr( LPCSTR sAttr, int nVal );
void XmlOutFormat( LPCSTR sFmt,... );
void XmlOut( LPCSTR sTxt );
void XmlOutLine( LPCSTR sTxt );
void ToFile( LPCSTR szFilename );
// Functions that don't affect m_sXml accumulator
static CString XmlFormat( LPCSTR sFmt,... );
static CString XmlElemWithAttrs( LPCSTR szTagName, LPCSTR szTagVal, int nAttrCnt, ... );
static CString XmlElem( LPCSTR sElem, LPCSTR sVal, BOOL fLineBreak=FALSE );
static CString XmlElem( LPCSTR sElem, int nVal, BOOL fLineBreak=FALSE );
static CString Quoted( LPCSTR sTxt );
static CString FixForXml( LPCSTR szText ) ;
static CString NumToStr( int n );
private:
CString m_sXML;
CString m_sTmpXML;
int m_nLenTmpXML;
BOOL m_fNothingIfBlank;
BOOL m_fAddLineBreaks;
};
And here's the C++ code file:
// XmlGen.cpp
// implements the CXmlGen class
//
#include "stdafx.h"
#include "XmlGen.h"
CXmlGen::CXmlGen( ) {
m_fNothingIfBlank= FALSE;
m_fAddLineBreaks= FALSE;
m_sXML= "";
m_sTmpXML.Preallocate( CNUM_nMaxLenTmpXML ); // see notes
m_sTmpXML="";
m_nLenTmpXML= 0;
}
CXmlGen::~CXmlGen() {
}
//-------------------------------------------------------
// Use printf-like formatting string, but %q means "quoted"
//
void CXmlGen::XmlOutFormat( LPCSTR szFmt,... )
{
CString sTxt;
va_list args; va_start(args, szFmt);
CString sFmt= szFmt;
sFmt.Replace("%q","\"%s\"" );
sTxt.FormatV( (LPCSTR)sFmt, args);
XmlOut( sTxt );
}
//--------------------------------------------------------
void CXmlGen::XmlOutLine( LPCSTR sTxt )
{
CString sTmp= sTxt; sTmp+="\r\n";
XmlOut( sTmp );
}
//--------------------------------------------------------
void CXmlGen::SetXml( LPCSTR szTxt )
{
m_sXML= szTxt;
m_sTmpXML= "" ;
m_nLenTmpXML= 0;
}
//--------------------------------------------------------
CString CXmlGen::GetXml()
{
m_sXML += m_sTmpXML;
m_sTmpXML= "" ;
m_nLenTmpXML= 0;
return( m_sXML );
}
//--------------------------------------------------------
// Append some output; Manage the temporary string
void CXmlGen::XmlOut( LPCSTR sTxt )
{
m_sTmpXML += sTxt;
if ( m_fAddLineBreaks ) {
m_sTmpXML += "\r\n";
}
m_nLenTmpXML += strlen( sTxt );
if ( m_nLenTmpXML > CNUM_nMaxLenTmpXML ) {
m_sXML += m_sTmpXML;
m_sTmpXML= "" ;
m_nLenTmpXML= 0;
}
}
//---------------- these output nothing if nVal is -1 or szVal is ""
//
void CXmlGen::XmlOutTag( LPCSTR szElem ) { // output a Tag only
XmlOutFormat( "<%s>", szElem );
}
void CXmlGen::XmlOutElem( LPCSTR szElem, LPCSTR szVal ) {
if ( szVal[0] != 0 ) {
XmlOutFormat( "<%s>%s</%s>", szElem, (LPCSTR)szVal, szElem );
}
}
void CXmlGen::XmlOutElem( LPCSTR szElem, int nVal ) {
if ( nVal != -1 ) {
XmlOutElem( szElem, NumToStr( nVal ) );
}
}
void CXmlGen::XmlOutAttr( LPCSTR szAttr, LPCSTR szVal ) {
if ( szVal[0] != 0 ) {
XmlOutFormat( " %s=\"%s\"", szAttr, (LPCSTR)FixForXml(szVal) );
}
}
void CXmlGen::XmlOutAttr( LPCSTR szAttr, int nVal ) {
if ( nVal != -1 ) {
XmlOutAttr( szAttr, NumToStr( nVal ) );
}
}
//--------------------------------------------------------
void CXmlGen::ToFile( LPCSTR szFilename )
{
CFile cFile( szFilename, CFile::modeCreate | CFile::modeWrite );
CString sOut= GetXml();
cFile.Write( sOut, sOut.GetLength() );
}
//--------------------------------------------------------
// static utility functions
//
CString CXmlGen::Quoted( LPCSTR sText )
{
CString sRet= "\"";
sRet += sText;
sRet += "\"";
return( sRet );
}
//--------------------------------------------------------
CString CXmlGen::FixForXml( LPCSTR szText )
{
CString sRet= szText;
sRet.TrimRight(); sRet.TrimLeft(); // strip leading and trailing spaces
if ( sRet.FindOneOf("&><\"'") != -1 ) {
sRet.Replace("&","&");
sRet.Replace(">",">");
sRet.Replace("<","<");
sRet.Replace("\"",""");
sRet.Replace("'","'");
}
return( sRet );
}
//--------------------------------------------------------
CString CXmlGen::NumToStr( int n ) {
CString sRet;
sRet.Format("%d", n);
return( sRet );
}
CString CXmlGen::XmlFormat( LPCSTR szFmt,... )
{
CString sTxt;
va_list args; va_start(args, szFmt);
CString sFmt= szFmt;
sFmt.Replace("%q","\"%s\"" );
sTxt.FormatV( (LPCSTR)sFmt, args);
return ( sTxt );
}
CString CXmlGen::XmlElem( LPCSTR szElem, LPCSTR szVal, BOOL fLineBreak/*=FALSE*/ ) {
CString sRet="";
if ( szVal[0] != 0 ) {
sRet= XmlFormat( "<%s>%s</%s>", szElem, (LPCSTR)szVal, szElem );
if (fLineBreak) sRet += "\r\n";
}
return( sRet );
}
CString CXmlGen::XmlElem( LPCSTR szElem, int nVal, BOOL fLineBreak/*=FALSE*/ ) {
CString sRet="";
if ( nVal != -1 ) {
sRet= XmlFormat( "<%s>%d</%s>", szElem, nVal, szElem );
if (fLineBreak) sRet += "\r\n";
}
return( sRet );
}
//--------------------------------------------------------
// variable args. IMPORTANT: nAttrCnt must be accurate
//
CString CXmlGen::XmlElemWithAttrs( LPCSTR szTagName, LPCSTR szTagVal, int nAttrCnt, ... )
{
va_list pVarArg;
CString sRet, sClose;
sRet.Format("<%s ", szTagName );
sClose.Format("</%s>", szTagName );
CString sAttrName, sAttrVal;
va_start( pVarArg, nAttrCnt );
for ( int j=0; j<nAttrCnt; j++ ) {
try {
sAttrName= va_arg( pVarArg, LPCSTR);
sAttrVal= va_arg( pVarArg, LPCSTR);
}
catch( ... ) {
// LogErr("bad args in XmlElemWithAttrs" );
ASSERT(0); // catch during debug runs
sAttrName= sAttrVal="";
}
if ( sAttrVal > "" ) {
sRet += sAttrName + "=\"";
sRet += sAttrVal + "\" ";
}
}
va_end( pVarArg );
if ( CString(szTagVal) > "" ) { // lazy check for NULL or ""
sRet += ">";
sRet += szTagVal;
sRet += sClose; // e.g., "</Element>";
}
else {
sRet += "/>";
}
return( sRet );
}
Some notes about this object:
CXmlGen cXml;
cXml.LineBreaks( TRUE );
cXml.XmlOutFormat("<?xml version=%q encoding=%q?>\r\n", "1.0", "UTF-8" );
cXml.XmlOut("<Root>" );
cXml.XmlOutElem( "PersonData",
cXml.XmlElem( "FirstName", "Dan" )
+cXml.XmlElem( "LastName", "Rollins" )
+cXml.XmlElem( "Children", 2 )
+cXml.XmlElem( "HairStyle", "ponytail" )
);
CString sElemProduct1= cXml.XmlElemWithAttrs(
"Product", "Widget", 4,
"size", (LPCSTR)"large",
"color", (LPCSTR)"blue",
"data", (LPCSTR)"",
"rating", (LPCSTR)"7"
);
CString sElemProduct2= cXml.XmlElemWithAttrs(
"Product", "Gizmo", 1,
"size", (LPCSTR)"small"
);
cXml.XmlOutElem( "Products", sElemProduct1+sElemProduct2 );
cXml.XmlOut("</Root>" );
cXml.ToFile( "c:\\temp\\junk.xml" );
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<PersonData><FirstName>Dan</FirstName><LastName>Rollins</LastName><Children>2</Children><HairStyle>ponytail</HairStyle></PersonData>
<Products><Product size="large" color="blue" rating="7" >Widget</Product><Product size="small" >Gizmo</Product></Products>
</Root>
Or, as shown in a webbrowser:
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Commented:
Regards,
Pavel