Prerequisites
A knowledge of ATL, ADO and XML is a must.
Introduction
This is an article about XML and one of the ways that you can generate XML. I had this
project where the design was in the form of an 'n' tier and the application
demanded that, the data should be displayed using XSLT. The input the XSLT is
XML Data. All the Data for the project was stored in SQL Server 7.0 and we were
supposed to retrive the data convert the data to XML and give it for further
processing in the form of XSLT Transformation. We had to give out put in various
level of hirearchy.
The Database
The Database used for the demo is
the good old pubs database, one of the standard databases installed with SQL
Server.
You would have to change the location of the
your database server name in the demo. It is a constant defined in the
stdafx.h. The line to change is as follows:
#define CONNECTION_STRING L"Provider=sqloledb;Data Source=[your database server name here];Initial Catalog=pubs;"
The Class
Now about the class. The name of the class is CXMLGenerator
.
The whole class is encapsulated by an ATL Object because of the project
design. It has a member variable, a few static functions and other virtual
functions so as to facilitate the XML generation, it is far more flexible this
way.
All the datatypes are Automation compatible so as the facilitate the
scripting languages to call upon these objects.
This class uses a helper class/structure called ATTRIBUTE
which has 2 member variables by the name of strName
and
strValue
.
Both the member variables are of type _bstr_t
.
List of all the members of the class are as follows :
1. Member Variable
There is only one member variable which private to the class and is used to store DWORD
type of data. This is useful in the generation of XML if we want the XML to be generated dependent on some condition.
DWORD m_lParam;
2. Static Function
The function defenitions are as follows:
BSTR CreateElement(BSTR strElementName,ATTRIBUTE * AttributeList, BOOL bHasValue, BSTR strElementValue);
The above mentioned function is used to actually create the Element in an XML Document. First parameter is the Name of the element to be created, second is the List of attributes that the attribute can have. Iif there are no attributes required for the element then this value has to be NULL
.
The third parameter is used to check whether the Element is has any value of not. If this parameter is passed false then the last parameter is ignored and the element is self ended or empty tag. Else if the third parameter is true then the last parameter is taken as the value for the element and then the tag is ended with the element name.
For e.g. CreateElement ("author",NULL,false,"")
will return <author/>
.
While the same function if called with ATTRIBUTE
of type strName = "ID"
and strValue = "003"
and the last parameter as "Shobha De"
, CreateElement ("author",attributeList, true,"Shobha De")
then the output will look like this <author id="003">ShobhaDe</author>
BSTR XMLEncode(BSTR strEncode);
is used to encode the data according to the XML standards.
3. Virtual Function
List of function virtual in nature are as follows:
BSTR PostContent(_RecordsetPtr RS);
This function is used to do some post processing after the content of the XML Element is generated and before the element is closed.
BSTR PreContent(_RecordsetPtr RS);
This function is used to do some pre processing befire the content of the XML Element is generated and after the element is defined.
BSTR BeforeEnd();
This function is used to add more XML data if required after the XML is generated for a given set of records.
BSTR AfterStart();
This function is used to add more XML data if required before the XML is generated for a given set of records.
BOOL HasValue(BSTR strElementName, BSTR strFieldValue);
This function return true or false depending on whether the element should have a value or should the element be empty or self ending.
BSTR GetValue(BSTR strElementName, BSTR strFieldValue);
This function provides the flexibility to change the value of the element if required else returns the value passed to the function.
ATTRIBUTE * GetAttributeList(BSTR ElementName, _RecordsetPtr RS);
This function returns an array of ATTIRBUTE for the element if the attributes are required here. The attribute are in the form of Name Value pair.
BSTR GetElementName(BSTR FieldName);
This function returns the name of the element if we want to change the name depending on the parameter passed.
BSTR GenerateXML(_RecordsetPtr RS,BSTR strRootNode, BSTR strChildNode, DWORD lParam)
This function is the actual work horse of the XML Generation.
The code for the Fuction is as given below.
BSTR CXMLGenerator::GenerateXML(_RecordsetPtr RS, BSTR strRootNode, BSTR strChildNode, DWORD lParam)
{
CEnBSTR *strFieldName;
_bstr_t strNode;
_bstr_t strFinalString = _bstr_t("");
if(SysStringLen(strRootNode) != 0)
{
strFinalString = "<";
strFinalString += strRootNode;
strFinalString += ">";
strNode = GetNodeName(strRootNode);
}
m_lParam = lParam;
strFinalString += AfterStart();
long nFldCount = RS->Fields->GetCount();
strFieldName = new CEnBSTR[nFldCount];
for(long i=0; i < nFldCount; i++)
{
strFieldName[i] = RS->Fields->Item[i]->Name;
strFieldName[i].lower();
}
_bstr_t strFinalElement;
while(!RS->ADOEOF)
{
strFinalElement = "";
ATTRIBUTE * ChildAttribute = GetAttributeList(strChildNode,RS);
strFinalElement = PreContent(RS);
for(long i=0; i < nFldCount; i++)
{
_bstr_t strElement = GetElementName(strFieldName[i]);
if(SysStringLen(strElement) != 0)
{
_variant_t temp1, temp2;
ATTRIBUTE * EleAttribute = GetAttributeList(strElement,RS);
temp1 = RS->Fields->GetItem(i)->Value;
_bstr_t strElementValue;
if(temp1.vt != VT_NULL)
{
HRESULT hr = VariantChangeType (&temp2,&temp1, VARIANT_NOUSEROVERRIDE | VARIANT_ALPHABOOL,VT_BSTR); if (FAILED(hr))
{
return _bstr_t ("");
}
strElementValue = temp2.bstrVal ;
}
else
strElementValue = _bstr_t("");
BOOL bHasValue = HasValue(strElement,strElementValue);
if(bHasValue)
{
strElementValue = GetValue(strElement,strElementValue);
}
strFinalElement += CreateElement(strElement,EleAttribute,bHasValue,strElementValue);
if (EleAttribute)
delete [] EleAttribute;
}
}
strFinalElement += PostContent(RS);
strFinalString += CreateElement(strChildNode,ChildAttribute,SysStringLen(strFinalElement) ? TRUE : FALSE, strFinalElement);
if (ChildAttribute)
delete [] ChildAttribute;
RS->MoveNext();
}
strFinalString += BeforeEnd();
if(SysStringLen(strRootNode) != 0)
{
strFinalString += "<!- ;
strFinalString += strNode;
strFinalString += ">";
}
return strFinalString;
}
I would like the thank Morten Abrahamsen for the wrapper class CEnBSTR of _bstr_t
which I have used for manipulating strings.
This class by itself will generate a very simple form of XML. But for a complex hirearchy you will have to derive from this class and override the methods as required. I have used two such classes in the demo project. The code will execute except for the database server name which I have mentioned earlier.
Any comments and suggestions are always welcome. Happy XML Generating.