Руководство по программированию на С++ с использованием libxml++
From: Горшков Никита <wishmaster.nick@mail.ru..>
Newsgroups: email
Date: Mon, 7 Feb 2008 14:31:37 +0000 (UTC)
Subject: Руководство по программированию на С++ с использованием libxml++
Готов выслушать замечания и предложения по поводу перевода.
Версия перевода от 22.02.2008г.
Адрес оригинала: http://libxmlplusplus.sourceforge.net/docs/manual/html/index.html
Вступление
Это руководство по программированию на языке С++ с использованием
библиотеки libxml (с примерами).
Содержание
libxml++
- Установка
- UTF-8 и Glib::ustring
- Компиляция и линковка
Парсеры
- Парсер DOM
- Парсер SAX
- TextReader парсер
libxml++
libxml++ это С++ API для популярного XML-парсера libxml, написанное на
С. libxml отличается высокой производительностью и соответствием
основным стандартам, но его С API очень сложный, даже для простых
проектов. libxml++ является простым С++-подобным API, позволяющим
создавать простые проекты с простым исходным кодом.
В отличие от других С++-парсеров, он не требует придерживаться
стандартных особенностей С++, таких как пространства имен,
STL-контейнеры или идентификация типа времени выполнения (runtime type
identification, прим. пер.), и не требует соответствия API-стандартам
предназначенным для Java. Поэтому libxml++ требует современного С++
компилятора подобного g++ 3.
В основном, libxml++ создавался для удовлетворения потребности в
надежном API и надежном ABI (ABI - Application Binary Interface, прим.
пер.) С++ XML-парсере, который смогли бы использовать в бинарном виде
как динамически подключаемую библиотеку к приложениям написанным на
языке С++, распределенным по разным адресным пространствам. Поэтому,
при обновлении libxml++ на более новую версию, все программы
использующие старую версию продолжают работать. libxml++ API
развивается постепенно за счет добавления нового кода в не содержащий
ошибок код API, а также из-за возможности параллельного использования
нескольких версий ABI одновременно. Это основные технологии и
принципы, на которых основан проект [12]GNOME , частью которого
является libxml++.
Установка
libxml++ содержится во многих Linux и *BSD дистрибутивах, но также
может быть установлен из исходных текстов под Linux и Windows. Для
компиляции подойдет любой современный компилятор, например g++, SUN
Forte, или MSVC++.
Для примера, установим libxml++ и его документацию на платформу
Debian, используя apt-get (также подойдет synaptic):
# apt-get install libxml++2.6-dev libxml++2.6-doc
Чтобы узнать, установили ли вы пакет libxml++ или что он должным
образом функционирует, используйте команду
pkg-config libxml++-2.6 --modversion.
Исходные тексты могут быть загружены с сайта
http://libxmlplusplus.sourceforge.net.
libxml++ поставляется под лицензией LGPL, которая разрешает
использовать его как динамически подключаемую библиотеку в открытом и
закрытом программном обеспечении. Лежащая в основе библиотека libxml
использует еще более богатую лицензию MIT.
UTF-8 и Glib::ustring
libxml++ API понимает строки в кодировке UTF-8 Unicode, которая может
поддерживать все известные языки и локали. UTF-8 был выбран, потому
что из всевозможных кодировок ему часто отдают предпочтение. UTF-8 это
мульти-байтовая кодировка, при этом, используемое значение некоторых
символов более 1 байта. Но для совместимости, строки "старого стиля"
представленные 7-ми битной кодировкой ASCII остаются без изменений при
кодировании в UTF-8, а UTF-8 строки не содержат нулевой байт, который
бы мешал оценивать число байтов. По этим причинам, вы можете
"спрятать" UTF-8 строки в объект std::string. К тому же, std::string
API может оперировать со строками на основе байтов, вместо символов.
Так как Стандарт С++ не имеет строкового класса, который бы мог
полностью оперировать с UTF-8, libxml++ использует класс Glib::ustring
из библиотеки glibmm. Glib::ustring содержит практически полностью
похожее на std::string API, но методы такие, как length() и operator[]
оперируют с целыми символами UTF-8, а не с одиночными байтами.
Возможно скрытое преобразование между std::string и Glib::ustring,
поэтому, вы везде сможете использовать std::string в любом случае
видимый через Glib::ustring в API, но только, если вы действительно не
заботитесь о различных локалях на любом другом языке, отличном от
английского. Однако, это очень маловероятно в нашем сегодняшнем
связанном мире!
glibmm также поддерживает полезный API для преобразования между
кодировками и локалями.
Компиляция и линковка
Для использования libxml++ в своем приложении, вы должны сообщить
компилятору, где искать хидеры и библиотеку libxml++. Для упрощения
процедуры сборки, libxml++ снабжена файлом pkg-config.pc. Для примера,
следующая команда включает нужные опции компилятора:
pkg-config libxml++-2.6 --cflags --libs
В том случае, если вы используете autoconf и automake, то наиболее
просто их использовать с макросом PKG_CHECK_MODULES в вашем
конфигурационном файле configure.ac. Для примера:
Также как и дочерняя библиотека libxml, libxml++ поддерживает три
парсера, которые вам могут потребоваться - DOM, SAX и TextReader
парсер. Их особенности и принципы работы с ними будут объяснены ниже.
Все эти парсеры могут анализировать XML-документы непосредственно на
диске, строки или C++ std::istream. Не смотря на то, что libxml++ API
использует только Glib::ustring, а следовательно кодировку UTF-8,
libxml++ может разбирать документы в любых кодировках, конвертируя в
UTF-8 автоматически. Это преобразование не приводит к потерям
какой-либо информации, потому что UTF-8 распознает любые локали.
Помните, что в XML-документах, как правило, "пустое место" играет
роль, поэтому парсеры могут находить неожиданные узлы, где текст
содержит "пустоту" или перевод каретки. Парсер не может знать, нужны
ли вам эти узлы или нет, но ваше приложение может игнорировать их.
Парсер DOM
Парсер DOM анализирует весь документ полностью и сохраняет его
структуру в памяти. Доступен через
Parser::get_document(). При помощи методов Document::get_root_node() и
Node::get_children(), вы можете перемещаться по иерархии дерева XML
без ограничений, перепрыгивая назад или вперед по документу
основываясь на информации, с которой вы сталкиваетесь в данный момент.
Поэтому парсер DOM использует большее количество памяти, чем
остальные.
Вы должны использовать C++ RTTI (через dynamic_cast<>) для определения
типа узла и выполнения действий, которые возможны с данным типом узла.
В примере только Element имеет атрибуты. Здесь представлена
наследственная иерархия типов узлов:
* xmlpp::Node:
+ xmlpp::Attribute
+ xmlpp::ContentNode
o xmlpp::CdataNode
o xmlpp::CommentNode
o xmlpp::ProcessingInstructionNode
o xmlpp::TextNode
+ xmlpp::Element
+ xmlpp::EntityReference
Несмотря на это, вы сможете получать указатели на Узлы, которые всегда
наследуются от своих родительских Узлов. В таком случае, это будет
означать, что данный Узел существует и ваш указатель на него будет
действительным пока экземпляр Документа существует.
Некоторые методы к тому же могут создавать новый дочерний Узел.
Используя его или один из методов Document::write_*(), вы можете
использовать libxml++ для построения нового XML-документа.
Пример
Этот пример анализирует документ, изучает его элементы и проверяет их.
Все приведенные здесь примеры содержатся в поставке исходных текстов
libxml++.
if(nodeText && nodeText->is_white_space()) //Let's ignore the indenting - you don't always want to do this.
return;
Glib::ustring nodename = node->get_name();
if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
{
print_indentation(indentation);
std::cout << "Node name = " << node->get_name() << std::endl;
std::cout << "Node name = " << nodename << std::endl;
}
else if(nodeText) //Let's say when it's text. - e.g. let's say what that white space is.
{
print_indentation(indentation);
std::cout << "Text Node" << std::endl;
}
if(!nodeContent)
{
//Recurse through child nodes:
xmlpp::Node::NodeList list = node->get_children();
for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
{
print_node(*iter, indentation + 2); //recursive
}
}
}
int main(int argc, char* argv[])
{
Glib::ustring filepath;
if(argc > 1 )
filepath = argv[1]; //Allow the user to specify a different XML file to parse.
else
filepath = "example.xml";
try
{
xmlpp::DomParser parser;
parser.set_validate();
parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
parser.parse_file(filepath);
if(parser)
{
//Walk the tree:
const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
print_node(pNode);
}
}
catch(const std::exception& ex)
{
std::cout << "Exception caught: " << ex.what() << std::endl;
}
return 0;
}
Парсер SAX
Парсер SAX представляет каждый узел XML-документа как
последовательность. Так, когда вы обрабатываете один узел, вы можете
получить информацию обо всех предыдущих узлах, но, с другой стороны, у
вас не будет никакой информации о последующих узлах. Парсер SAX
использует меньше памяти, чем парсер DOM и подходит для пакетной
обработки документов.
При использовании метода parse_chunk()вместо parse(), вы можете
разбирать XML-документ по частям перед его общей обработкой.
Как показано в примере, вы должны получить ваш родительский класс из
SaxParser и заменить некоторые из виртуальных методов. Эти методы
"указателя" (в оригинале "handler" прим. пер.) будут вызваны, когда
документ проанализирован.
Пример
Этот пример показывает, как методы указателя вызываются во время
анализа.
int
main(int argc, char* argv[])
{
Glib::ustring filepath;
if(argc > 1 )
filepath = argv[1]; //Allow the user to specify a different XML file to parse.
else
filepath = "example.xml";
// Parse the entire document in one go:
try
{
MySaxParser parser;
parser.set_substitute_entities(true); //
parser.parse_file(filepath);
}
catch(const xmlpp::exception& ex)
{
std::cout << "libxml++ exception: " << ex.what() << std::endl;
}
// Demonstrate incremental parsing, sometimes useful for network connections:
{
//std::cout << "Incremental SAX Parser:" << std:endl;
Подобно парсеру SAX, парсер TextReader подходит для пакетной
обработки, но вместо использования указателей на определенные части
документа, он позволяет определять тип текущей ветви, обработать ветвь
соответствующим образом и пропустить в документе столько, сколько
необходимо. Не так, как при использовании парсера DOM, вы не сможете в
XML-документе вернуться назад. С другой стороны, у вас не будет
расточительной по времени обработки ветвей, которые вам не нужны, что
наблюдается при использовании парсера SAX.
Все методы находятся в одном объекте синтаксического анализатора, но
их результат зависит от данной ситуации. Например, используйте
read()для перемещения к следующей текстовой ветви, а move_to_element()
для навигации по дочерним ветвям. Эти методы возвращают false, если
доступных узлов больше нет. Тогда используйте один из методов
get_name() и get_value() для проверки элементов и их атрибутов.
Пример
Этот пример проверяет каждый узел по очереди, а затем перемещается на
следующий.