takminの書きっぱなし備忘録 @はてなブログ

主にコンピュータビジョンなど技術について、たまに自分自身のことや思いついたことなど

Xerces_C++でXML読み込みの簡単なプログラムを組んでみた

Xercesっていうのは、Apache Software Foundationで開発されている、オープンソースXMLパーサーのこと。
Java版がメジャーだけど、C++XML読み込む必要があったので、実装してみた。
以下、Windows + Visual Studioでの手順のメモ書き。備忘録なので、かなりはしょってます。

また、とりあえず動けばいいや、で組んだプログラムなので、結構いい加減です。参考にする場合は自己責任でお願いします。


1.ソースのダウンロードとコンパイル

http://xerces.apache.org/xerces-c/download.cgi

で、xerces-c-3.0.1.zip(ソース)をダウンロード。
解凍した後、"xerces-c-3.0.1\xerces-c-3.0.1\projects\Win32\VC8\xerces-all"へ移動。
xerces-all.slnを起動して、普通にReleaseビルド。


2.実行環境作成
・出力されたxerces-c_3.libを適当なディレクトリへコピー。(ex. <開発ディレクトリ>\lib)
・xerces-c_3_0.dllを開発ディレクトリの直下へコピー。
・解凍したソースコードディレクトリの"xerces-c-3.0.1\src\xercesc"ディレクトリを適当な場所(ex. <開発ディレクトリ>\inc)へコピー
Visual Studioのプロジェクトのプロパティを開き、左側のツリーから「構成プロパティ -> C/C++ -> 全般」を選択して、追加のインクルードディレクトリに、インクルードファイルをコピーした場所を追加(ex. inc)
・リンカの方にも同じくxerces-c_3.libへのライブラリパスとライブラリ名を追加しておく。


3.ソースコード
次のようなXMLファイルを読み込むプログラムを作る。


	VOC2007
	2007_000033.jpg
	
		The VOC2007 Database
		PASCAL VOC2007
		flickr
		n/a
	
	
		n/a
		n/a
	
	
		500
		366
		3
	
	1
	
		aeroplane
		Unspecified
		0
		0
		
			9
			107
			499
			263
		
	
	
		aeroplane
		Left
		0
		0
		
			421
			200
			482
			226
		
	
	
		aeroplane
		Left
		1
		0
		
			325
			188
			411
			223
		
	

これを、タグの中身を取得して、以下のようなVOC_OBJECT構造体へ格納するプログラムを書きたい。

typedef enum{
	FRONTAL, REAR, LEFT, RIGHT
}OBJECT_POSE;

typedef struct{
	char name[128];
	OBJECT_POSE pose;
	int truncate;
	int difficult;
	int xmax;
	int xmin;
	int ymax;
	int ymin;
}VOC_OBJECT;

以下、ソースコードXMLパーサにはDOMとSAXという2つの種類があるんだけど、ここではDOMを使いました。DOMとSAXの違いについては、この記事でも参考にしてください。

#include 
#include 
#include 
#include 
using namespace xercesc;

#include 
using namespace std;

/***** tagNameという名前の子要素を取得 *****/
DOMNodeList* getElementByCharTagName(DOMElement* elem, char *tagName)
{
	XMLCh* attributeName=XMLString::transcode(tagName);
	DOMNodeList* nlist = elem->getElementsByTagName(attributeName);
	XMLString::release(&attributeName);

	return nlist;
}

/***** tagNameという名前の子要素からその中身(int型)を取得 *****/
int getIntByCharTagName(DOMElement* elem, char *tagName)
{
	DOMNodeList* nlist = getElementByCharTagName(elem,tagName);
	const XMLCh* node_txt = ( (nlist->item(0))->getFirstChild() )->getNodeValue();
	char* str = XMLString::transcode(node_txt);
	int ret = atoi(str);
	XMLString::release(&str);

	return ret;
}


// XMLファイルの読み込み
void readXML(char* xmlFile)
{
	/* Xercesの初期化(必須)*/
	try {
		XMLPlatformUtils::Initialize();
	}
	catch (const XMLException& toCatch) {
		// Do your failure processing here
		return;
	}

    XercesDOMParser *parser = new XercesDOMParser;
	VOC_OBJECT*	voc_obj = 0;
	int obj_num = 0;

	try {
		parser->parse(xmlFile);
		DOMDocument *doc = parser->getDocument();
		DOMElement *root = doc->getDocumentElement();

		DOMNodeList* nlist = getElementByCharTagName(root,"object");

		obj_num = (int)(nlist->getLength());
		voc_obj = new VOC_OBJECT[obj_num];

		char *str;
		DOMNodeList *nlist2;
		const XMLCh *node_txt;
		for(int i=0;i(nlist->item(i)),"name");
			node_txt = ( (nlist2->item(0))->getFirstChild() )->getNodeValue();
			str = XMLString::transcode(node_txt);
			strcpy(voc_obj[i].name, str);
			XMLString::release(&str);

			nlist2 = getElementByCharTagName(dynamic_cast(nlist->item(i)),"pose");
			node_txt = ( (nlist2->item(0))->getFirstChild() )->getNodeValue();
			str = XMLString::transcode(node_txt);
			if(!strcmp(str,"Frontal")){
				voc_obj[i].pose = FRONTAL;
			}
			else if(!strcmp(str,"Rear")){
				voc_obj[i].pose = REAR;
			}
			else if(!strcmp(str,"Left")){
				voc_obj[i].pose = LEFT;
			}
			else if(!strcmp(str,"Right")){
				voc_obj[i].pose = RIGHT;
			}
			XMLString::release(&str);

			voc_obj[i].truncate = getIntByCharTagName(dynamic_cast(nlist->item(i)),"truncated");
			voc_obj[i].difficult = getIntByCharTagName(dynamic_cast(nlist->item(i)),"difficult");

			nlist2 = getElementByCharTagName(dynamic_cast(nlist->item(i)),"bndbox");
			voc_obj[i].xmin = getIntByCharTagName(dynamic_cast(nlist2->item(0)),"xmin");
			voc_obj[i].xmax = getIntByCharTagName(dynamic_cast(nlist2->item(0)),"xmax");
			voc_obj[i].ymin = getIntByCharTagName(dynamic_cast(nlist2->item(0)),"ymin");
			voc_obj[i].ymax = getIntByCharTagName(dynamic_cast(nlist2->item(0)),"ymax");
		}
	} catch(...) {
		cerr << "ファイルの解析に失敗しました。" << endl;
	}

	/* 読み込み結果の出力 */
	for(int i=0;i0)
		delete [] voc_obj;
	delete parser;

	/* Xercesの終了(必須)*/
	XMLPlatformUtils::Terminate();
}

4.解説など
めんどくさいので、細かいソースの解説はしません。一応、簡単なTipsだけ載せます。

  • 必ずメインのソースの頭と後ろにXercesの初期化と終了のコードを入れる
  • Xercesでは、文字列はXMLChという型を独自に定義して使っているので(実態はunsigned short型)、それをchar型へ変換する場合は、"XMLString::transcode"という関数を使ってやり、また取得したcharは"XMLString::release"で解放する。
  • DOMの扱い方については、C++に関する日本語ドキュメントは少ないが、Javaに関するものは豊富にあるので、結構参考になる。