Tegyük fel, hogy C++-ban szeretnénk XML file-t gyártani. Ehhez kedvenc Microsoft-unk mellékeli az MSXML könyvtárat. Sajna az MSXML save függvénye egyetlen sorban írja ki az XML-t. Ez eléggé olvashatatlanná silányítja a terméket. 4.0-ás MSXML-lel kellett ezt valami értelmes módon megoldanom.
Gugliztam egy kicsit és találtam egy megoldást >itt<. Én botor módon azt hittem, hogy kb. egy utasítással meg lehet oldani, de nem! A fél kalevalát be kell kopipasztázni!
A végén meg át is kellett alakítanom, mert sehogy sem sikerült file-ba masszíroznom az eredményt. Sajnos sok a nem dokumentált történés, de végül sikerült és azóta boldogan használom. Remélem másoknak is segít ez a kis szösszenet.
Először is nézzük a szükséges fejlécelést és globális ügymenetet. VS2010 alatt fordítva műxik.
#include "msxml2.h"
#include <iostream>
#include <fstream>
...
MSXML2::IXMLDOMDocument2Ptr pDoc;
...
#define ifCOMFailed(ret) if (FAILED(hr = ret)) { \
::MessageBox(NULL, _com_error(hr).ErrorMessage(), _T("Error"), MB_OK); \
return 0; }
A pDoc-ba az XML fa kerül bele. Az ifCOMFailed macro csak egy kis kódrövidítés (bocs érte!). Az ez után levők egy függvénybe foglalandó, hogy a macro return 0-ja érvényesülni tudjon... A kód első fele egy UTF-16 kódolású variant string-be szuszakolja a beformázott anyagot. Bevallom, hogy nem értem, hogy mit csinál, de működik!// MXWriter létrehoz
CComPtr <IMXWriter> pMXWriter;
ifCOMFailed(pMXWriter.CoCreateInstance(__uuidof (MXXMLWriter),
NULL, CLSCTX_ALL));
CComPtr <ISAXContentHandler> pISAXContentHandler;
ifCOMFailed(pMXWriter.QueryInterface(&pISAXContentHandler));
CComPtr <ISAXErrorHandler> pISAXErrorHandler;
ifCOMFailed(pMXWriter.QueryInterface(&pISAXErrorHandler));
CComPtr <ISAXDTDHandler> pISAXDTDHandler;
ifCOMFailed(pMXWriter.QueryInterface(&pISAXDTDHandler));
ifCOMFailed(pMXWriter->put_indent(VARIANT_TRUE));
// Encoding-ot nem használja az IMXWriter, ha BSTR a kimenet.
// UTF-16-ot ír be helyette.
//ifCOMFailed(pMXWriter->put_encoding(L"utf-8"));
VARIANT output;
output.vt = VT_EMPTY; // Default: BSTR output
ifCOMFailed(pMXWriter->put_output(output));
// SAX reader létrehoz
CComPtr <ISAXXMLReader> pSAXReader;
ifCOMFailed(pSAXReader.CoCreateInstance(__uuidof(SAXXMLReader),
NULL, CLSCTX_ALL));
ifCOMFailed(pSAXReader->putContentHandler(pISAXContentHandler));
ifCOMFailed(pSAXReader->putDTDHandler(pISAXDTDHandler));
ifCOMFailed(pSAXReader->putErrorHandler(pISAXErrorHandler));
ifCOMFailed(pSAXReader->putProperty(L"http://xml.org/sax/properties/lexical-handler", CComVariant (pMXWriter)));
ifCOMFailed(pSAXReader->putProperty(L"http://xml.org/sax/properties/declaration-handler", CComVariant (pMXWriter)));
// Kiír
ifCOMFailed(pSAXReader->parse(CComVariant(&*pDoc)));
ifCOMFailed(pMXWriter->get_output(&output));
Sajnos pár dolgot át kell alakítani. Tab-okat használ, ezeket 2 szóközre cseréltem. A több soros commenteket több egysoros commentre cseréli. Nem értem, hogy miért!
CString s = (LPCTSTR)output.bstrVal;
Most már csak ki kellene írni. Ha így hagyjuk, akkor az UTL-16 BOM-ot elé írva már ki is írhatjuk, de én inkább UTF-8-ban szeretném. Ehhez még egy pici masszírozni kell a kódot.
s.Replace(_T("\t"), _T(" ")); // tab túl széles. Legyen 2 szóköz
s.Replace(_T("-->\r\n<!--"), _T(" ")); // Comment sorok széttörve
s += _T("\r\n"); // Üres sorvég hozzáad
s.Replace(_T("\"UTF-16\""), _T("\"UTF-8\""));
string buffer = CT2CA(s, CP_UTF8);
try {
ofstream xml_out(flName, ios_base::out | ios_base::trunc | ios_base::binary);
xml_out << buffer;
xml_out.close();
} catch(const exception &e) {
::MessageBox(NULL, CString(e.what()), _T("Save Error"), MB_OK);
return 0;
}
Ez végre egy szépen beformázott file-t hoz létre.
Formázódjunk minden nap!
+jegyzések