This short tutorial shows how easy it’s to generate reports in HTML pages using Microsoft XML DOM API together XML and XSLT.
XML (Extensible Markup Language) became a universal standard of encoding data in a format that is both human-readable and machine-readable. It’s widely used in business applications and even Microsoft Office uses it into internal file formats.
XSLT is used for XML documents decoration. Once we have data into a XML files, using the XSLT (Extensible Stylesheet Language Transformations) we can easily generate HTML and xHTML files. XSLT is a W3C recommendation still from 16. November 1999 and in the meantime, it was extended with a new version XSLT v2.0.
XSLT uses XPATH to get the XML’s tags information, complete the predefined temples and transform results into a .html document.
Each decent browser has support for XML and XSLT. All we have to do it’s to link two such files (.xml and .xslt) and once we execute the XML file the browser will generate and render our XHTML content.
// content of XML file <!--?xml version="1.0" encoding="utf-8"?--> <!--?xml-stylesheet href="sample_1.xslt" type="text/xsl"?--> // the rest of the XML file
But in case we are writing non-browser applications the HTML generation becomes a bit complicated in case you are not satisfied with a hard-coded solution and want a flexible solution.
Using the Microsoft’s XML Core Services (MSXML) our job became a piece of cake. We focus once over the HTML generator and later in case we want to change something into our look and content we have to deal only with the .xml and .xslt files.
bool CHTMLGen::Generate(const std::wstring& sXmlFile, const std::wstring& sXsltFile, const std::wstring& sHTMLFile) { if (!PathFileExists(sXmlFile.c_str()) || !PathFileExists(sXsltFile.c_str())) { return false; } HRESULT hr; CComPtr pXml, pXslt; hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXml); if (FAILED(hr)) return false; VARIANT_BOOL bOkLoad; CComVariant varFile; varFile = sXmlFile.c_str(); pXml->put_async(VARIANT_FALSE); hr = pXml->load(varFile, &bOkLoad); if (FAILED(hr) && (bOkLoad == VARIANT_FALSE)) return false; hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXslt); if (FAILED(hr)) return false; varFile = sXsltFile.c_str(); pXslt->put_async(VARIANT_FALSE); hr = pXslt->load(varFile, &bOkLoad); if (FAILED(hr) && (bOkLoad == VARIANT_FALSE)) return false; CComBSTR bsHtmlRes; std::wstring sHTMLRes = _T(""); hr = pXml->transformNode(pXslt, &bsHtmlRes); if (SUCCEEDED(hr)) { CAtlString sRes(bsHtmlRes); if (!_tcsnicmp(sRes, _T("")); sHTMLRes += szEnd ? (szEnd + 2) : sRes; } else sHTMLRes += sRes; } return (_T("") != sHTMLRes) ? SaveFileContent(sHTMLFile, sHTMLRes) : false; }
Because of using COM don’t forget proper the calls of CoInitialize() and CoUninitialize().
Here are two samples files generated with the test application using the upper method: sample_1, sample_2.
The combination of XML, XSLT and XPATH offers a very flexible way to generate HTML files. With such an approach even native application does not need to change in case we change the HTMLs look. Within the presented case the hard-coded solutions are avoided and most probably a new recompilation is not needed in case we want to change data content (XML) or the look (XSLT).
In case you want to add sophisticated HTML code (ex. colored, formatted, images, etc.) you need to convert that code into XHTML format before adding data into the .XSLT file.