RapidXML

XML ファイルを解析するときは(それしか知らないので)Ruby を使ってやっていたのですが,C++ のクラスの中に XML の解析部を組み込む必要が出てきたので真面目に C++XML パースライブラリを探すことにしました.

togeの日記 でいくつかの XML パースライブラリが紹介されていましたが,最近のものだと以下の 2 つが使いやすいようです.

今回は,これらのうち RapidXML を使ってみました.

サンプルコード

インストールは,ダウンロードして解凍するだけなので特に難しい所はなさそうです.サンプルコードは何にしようかなと悩んだのですが,ちょっと前に Ruby で OpenXML ファイルを整形するスクリプト を書いたので,これを RapidXML を用いて C++ で書いてみる事にします.

#include <iostream>
#include <string>
#include "rapidxml/rapidxml.hpp"
#include "rapidxml/rapidxml_utils.hpp"

class format_openxml {
public:
    format_openxml() : level_(0) {}
    
    template <class XMLNode>
    void run(XMLNode* node) {
        std::string indent;
        for (int i = 0; i < level_; ++i) indent += '\t';
        
        std::cout << indent << '<' << node->name();
        for (rapidxml::xml_attribute<>* attr = node->first_attribute();
            attr; attr = attr->next_attribute()) {
            std::cout << " " << attr->name() << "=\"" << attr->value() << "\"";
        }
        
        XMLNode* child = node->first_node();
        if (child && child->name() && child->name_size() > 0) {
            std::cout << '>' << std::endl;
            for (; child; child = child->next_sibling()) {
                ++level_;
                this->run(child);
                --level_;
            }
            std::cout << indent << "</" << node->name() << '>' << std::endl;
        }
        else if (node->value_size() > 0) {
            std::cout << '>' << node->value() << "</" << node->name() << '>' << std::endl;
        }
        else std::cout << " />" << std::endl;
    }
    
private:
    int level_;
};

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "format_openxml filename" << std::endl;
        std::exit(-1);
    }
    
    rapidxml::file<char> in(argv[1]);
    try {
        rapidxml::xml_document<char> doc;
        doc.parse<0>(in.data());
        
        format_openxml fmt;
        fmt.run(doc.first_node());
    }
    catch (rapidxml::parse_error& e) {
        int pos = reinterpret_cast<int>(e.where<char>()) - reinterpret_cast<int>(in.data());
        std::cerr << "parse error (" << pos << ") : " << e.what() << std::endl;
        std::exit(-1);
    }
    
    return 0;
}

このように Ruby とほぼ同じような形で書けました.使いやすくて良い感じです.デフォルト (char) は typedef しておいてユーザに <> は書かせない方が良かったのでは,とは思いますが.