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に置き換えてください。