Дневник Шестеро Михаила

Oct. 7th, 2014

04:29 pm - Чтение простого XML с помощью RapidXML

Я люблю хранить технологическую конфигурацию моих программ и утилит в XML-файле.
Пока я делал приложения с использованием Qt использовал небольшой класс, который читал эту конфигурацию и сохранял её в QStringMap.

Однако возникла потребность сделать простую утилиту на C++ но без Qt, XML-парсером которой я пользовался.
Типичный формат моего XML таков:

<?xml version="1.0" encoding="utf-8"?>
<registry>
    <version>1</version>
    <section id="common" >
        <parameter id="locale" >ru_RU.utf8</parameter>

        <parameter id="parameter1" >value1</parameter>
        <parameter id="parameter2" >value2</parameter>
    </section>
</registry>
Для чтения я без проблем воспользовался бесплатной библиотекой RapidXML 1.13. Сейчас она входит в Boost, но я попробовал также использовать её и отдельно (при сборке с помощью GNU C++/MinGW под Windows). Собственно библиотека эта состоит из трёх шабонных HPP-файлов, которые не требуют компиляции.

Публикую код, который читает XML и сохраняет конфигурацию в STL-коллекцию:
//#include <rapidxml.hpp>
//using namespace rapidxml;
#include <boost/property_tree/detail/rapidxml.hpp>
using namespace boost::property_tree::detail::rapidxml;

// .................

    // ---
    xml_document<char> doc;

    //cout << "Parse..." << endl;

    try {
        ifstream file( fname_xml.c_str() );
        vector<char> buffer((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
        buffer.push_back('\0');

        doc.parse<0>( &buffer[0] );
    }
    catch (parse_error e) {
        cerr << "Fail. Parse error " << e.what() << endl;
        return 0;
    }

    xml_node<> *root_node = doc.first_node();
    //cout << "Root found?" << endl;

    if (root_node!=NULL)
    {
        //cout << "Root found!" << root_node->name() << ":" << root_node->value() << endl;
        xml_node<> *node = root_node->first_node("section");
        //if (node!=NULL) cout << "section found" << endl;
        if (node) node = node->first_node("parameter");
        //if (node!=NULL) cout << "first parameter found" << endl;
        while (node!=NULL)
        {
            xml_attribute<>* a = node->first_attribute("id");
            if (a!=NULL)
            {
                config[ a->value() ] = node->value();
            }

            node = node->next_sibling();
        }
    }
    cout << "========" << endl << "Check loaded configuration:" << endl;

    typedef map<string,string>::const_iterator it;
    for (it i=config.begin(); i!=config.end(); i++)
    {
        cout << i->first << "\t= " << i->second << endl;
    }
    cout << "Configuration loaded" << endl;
    // ---
    

Стоит добавить, что когда я подставлял в doc.parse<0>( ... ) для проверки забитую в коде текстовую константу, это приводило к падению программы во время работы парсера (run-time exception). Так получалось потому, что RapidXML для скорости применяет "разрушающий" разбор - точнее в процессе работы он заменяет символы после концов символьных сущонстей XML нулевыми байтами. Таким образом они превращаются в "отдельные" строки с точки зрения C/C++ без копирования их содержимого.

Tags: , , , , ,
Current Mood: busy