Xercesが変わった!?
はい、Xerces-C 2.x から、かなり大きく変わりました。少なくとも従来のコードを’そのまま’コンパイルできなくなっています。 それ以外にも細かな相異がいくつかあります。
とりあえず昔のままで…
インタフェースが大きく変わってしまったのですが、幸いにも旧いインタフェースは残されています。従来のinclude:
#include <xercesc/dom/DOM.hpp>
は、
#include <xercesc/dom/deprecated/DOM.hpp>
のように .../deprecated
に置かれていますから、#include
の修正だけで’とりあえず’はコンパイルできます。
とはいえ、deprecated には違いないので早めに新たなインタフェースで書き直すことをお薦めします
ICUを使わせるには?
Xerces-CにICUを使わせるには?と全く同じ手順で使えるようになります。すなわち:
- ICU
-
- ICU 2.2 を適当なディレクトリに展開し、そのディレクトリ・パスを環境変数 ICU_ROOT に設定しておく。
-
$(ICU_ROOT)/source/allinone/allinone.dsw で build
$(ICU_ROOT)/bin に DLL ができるから、PATHを通す。
- Xerces-C 2.x
-
-
Xerces-C 2.0.0 のソース版を適当なディレクトリに展開する。
- (VC++の場合) IDEから Projects/Win32/VC6/xerces-all/xerces-all.dsw を開け、XercesLibをbuild-targetとする。
-
プロジェクト設定を以下のとおり変更:
- include-path に $(ICU_ROOT)¥include を追加
- マクロ XML_USE_WIN32_TRANSCODER 改め XML_USE_ICU_TRANSCODER に
- library-path に $(ICU_ROOT)¥lib を追加。
- ライブラリ icuuc.lib (Debug版では icuucd.lib) を追加。
-
FileView: XercesLib/util/Transcoders にある Win32TransService.(hpp|cpp) を引っこ抜いて、跡地にsrc/xercesc/util/Transcoders/ICU/ICUTransService.(hpp|cpp) を投げ込む。
-
- build!
-
-
Build/Win32/VC6/(Release|Debug) に lib/dll ができあがる。
-
文字コード変換がICUに置き換わったことを確認するには、例えばiso-2022-jp(JIS)で書かれたXMLをパースしてください。
後述する”で、どう変わったのですか?”にあるコードがそれです。
標準C++ライブラリに依存しますか?
はい、依存します。そのために、標準C++ライブラリをたとえばSTLPortなどに取り替えるとなれば、Xerces-C自体も同じ環境で再構築しなければなりません。
Xerces-Cのソースコードを眺めてみたところ、標準C++依存部はたった一箇所でした。
以下に示すのは Xerces-C 2.1.0 の …/src/xercesc/framework/StdOutFormatTarget.cppです:
#include <xercesc/framework/StdOutFormatTarget.hpp> #include <iostream.h> #include <stdio.h> StdOutFormatTarget::StdOutFormatTarget() {} StdOutFormatTarget::‾StdOutFormatTarget() {} void StdOutFormatTarget::writeChars(const XMLByte* const toWrite , const unsigned int count , XMLFormatter* const formatter) { // Surprisingly, Solaris was the only platform on which // required the char* cast to print out the string correctly. // Without the cast, it was printing the pointer value in hex. // Quite annoying, considering every other platform printed // the string with the explicit cast to char* below. cout.write((char *) toWrite, (int) count); }
これを以下のように修正し、再構築してください。標準C++依存部分がなくなります:
#include <xercesc/framework/StdOutFormatTarget.hpp> #include <stdio.h> StdOutFormatTarget::StdOutFormatTarget() {} StdOutFormatTarget::‾StdOutFormatTarget() {} void StdOutFormatTarget::writeChars(const XMLByte* const toWrite , const unsigned int count , XMLFormatter* const formatter) { // Surprisingly, Solaris was the only platform on which // required the char* cast to print out the string correctly. // Without the cast, it was printing the pointer value in hex. // Quite annoying, considering every other platform printed // the string with the explicit cast to char* below. fwrite(toWrite, 1, count, stdout); }
v1.xで文字コードの変換が思わしくないのですが…
どうやらそのようです。v1.5〜v1.7あたりでは、UNICODEからnativeへの変換:DOMString::transcode()
の挙動に不審な点が見受けられます。
かわりに XMLString::transcode
をお薦めします。
/* * DOMString output を native に変換し、プリントする */ DOMString output; // UNICODE文字列 XMLCh wbuffer[32]; // 十分な大きさのバッファ(変換先) XMLString::copyNString(wbuffer, output.rawBuffer(), output.length()); wbuffer[output.length()] = L'¥0'; char* buffer = XMLString::transcode(wbuffer); std::cout << buffer << std::endl; delete[] buffer;
v2.xではDOMString
自体deprecateされたので、いずれにせよXMLString
を用いたコード変換が主流となるでしょう
で、どう変わったのですか?
従来(v1.x)のDOM_Node
はHandle/Bodyパターンを駆使することによってポインタを排除し、Java-likeなインテフェースを提供していました。しかしこのJava-likeなインタフェースはその実現のためパフォーマンスを落としていました。
v2.xからはこのJava-likeなインタフェースを一掃(deprecate)し、ポインタを’素’で見せる実装に改められました。
加えて従来の文字列 DOMString
もdeprecateされ、UNICODE文字列へのポインタ XMLCh*
に改められました。
以下は簡単なXMLドキュメントをパースし、出力するコードです(v1.7.0)
#include <string> #include <iostream> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/DOMParser.hpp> #include <xercesc/framework/MemBufInputSource.hpp> #include <xercesc/util/PlatformUtils.hpp> int main() { XMLPlatformUtils::Initialize(); { "<country>¥x1b¥x24¥x42¥x46¥x7c¥x4b¥x5c¥x1b¥x28¥x42</country>"; MemBufInputSource source((const XMLByte*)input.data(), input.size(), "memory_buffer", false); DOMParser parser; parser.parse(source); DOM_Document document = parser.getDocument(); DOM_Element element = document.getDocumentElement(); DOM_Text text = static_cast<DOM_Text&>(element.getFirstChild()); DOMString output = text.getData(); XMLCh wbuffer[32]; XMLString::copyNString(wbuffer, output.rawBuffer(), output.length()); wbuffer[output.length()] = L'¥0'; char* buffer = XMLString::transcode(wbuffer); std::cout << buffer << std::endl; delete[] buffer; } XMLPlatformUtils::Terminate(); return 0; }
同じものを v2.1.0 の新 API で書き直すと以下のようになります。
#include <string> #include <iostream> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <xercesc/framework/MemBufInputSource.hpp> #include <xercesc/util/PlatformUtils.hpp> int main() { XMLPlatformUtils::Initialize(); { "<country>¥x1b¥x24¥x42¥x46¥x7c¥x4b¥x5c¥x1b¥x28¥x42</country>"; MemBufInputSource source((const XMLByte*)input.data(), input.size(), "memory_buffer", false); XercesDOMParser parser; parser.parse(source); DOMDocument* document = parser.getDocument(); DOMElement* element = document->getDocumentElement(); DOMText* text = static_cast<DOMText*>(element->getFirstChild()); char buffer[32]; XMLString::transcode(text->getData(), buffer, 32); std::cout << buffer << std::endl; } XMLPlatformUtils::Terminate(); return 0; }
ポインタはdeleteしなくていいのですか?
新APIで定義されたDOMNode
にはメソッドrelease()
が定義されており、これを使ってメモリの解放を行ないます
DOMNode* node; node->release();
このメソッドはノードの子ノードを再帰的にreleaseします
ただし、パーサからgetDocument
()で取り出したドキュメント、すなわちDOMNode
は、パーサのデストラクトと同時にrelease
されます。DOMNode
を温存したままパーサだけを先に廃棄するときは、パーサのメソッドadoptDocument
でドキュメントを取り出してください。
DOMDocument* document; { XercesDOMParser parser; parser.parse(...); document = parser.adoptDocument(); } ... document->release();
DOMNodeをファイルに出力するには?
DOMWriter
が装備されました。使用例を以下に示します。
#include <iostream> #include <locale> #include <xercesc/util/PlatformUtils.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/dom/DOMWriter.hpp> #include <xercesc/framework/StdOutFormatTarget.hpp> #include <xercesc/util/XMLUni.hpp> int main() { XMLPlatformUtils::Initialize(); std::locale::global(std::locale("japanese")); DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(L"LS"); DOMDocument* doc = impl->createDocument(0, L"会社",0); DOMElement* rootElem = doc->getDocumentElement(); DOMElement* prodElem = doc->createElement(L"製品"); rootElem->appendChild(prodElem); DOMText* prodDataVal = doc->createTextNode(L"Xerces-C"); prodElem->appendChild(prodDataVal); DOMElement* catElem = doc->createElement(L"品種"); rootElem->appendChild(catElem); catElem->setAttribute(L"アイデア", L"お見事"); DOMText* catDataVal = doc->createTextNode(L"XMLパーサ"); catElem->appendChild(catDataVal); DOMElement* devByElem = doc->createElement(L"作ったひと"); rootElem->appendChild(devByElem); DOMText* devByDataVal = doc->createTextNode(L"あぱっち"); devByElem->appendChild(devByDataVal); DOMWriter* writer = ((DOMImplementationLS*)impl)->createDOMWriter(); writer->setEncoding(L"shift_jis"); StdOutFormatTarget target; writer->writeNode(&target, *doc); delete writer; doc->release(); XMLPlatformUtils::Terminate(); return 0; }
この例では標準出力に出力しています。
ファイルに出力するには、StdOutFormatTarget
をLocalFileFormatTarget
に置き換えてください。