The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Руководство по программированию на С++ с использованием libxml++


<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>
From: Горшков Никита <wishmaster.nick@mail.ru..>
Newsgroups: email
Date: Mon, 7 Feb 2008 14:31:37 +0000 (UTC)
Subject: Руководство по программированию на С++ с использованием libxml++

Автор: Murray Cumming <murrayc@murrayc.com..>
Перевод: Горшков Никита <wishmaster.nick@mail.ru..> <ICQ: 278224209>

Готов выслушать замечания и предложения по поводу перевода.

Версия перевода от 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. Для примера:

            PKG_CHECK_MODULES(SOMEAPP, libxml++-2.6 >= 2.10.0)
            AC_SUBST(SOMEAPP_CFLAGS)
            AC_SUBST(SOMEAPP_LIBS)


Парсеры

   Также  как  и  дочерняя  библиотека  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++.

   Source Code: http://libxmlplusplus.sourceforge.net/examples/dom_parser
   File: main.cc


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <libxml++/libxml++.h>

#include <iostream>

void print_indentation(unsigned int indentation)
{
  for(unsigned int i = 0; i < indentation; ++i)
    std::cout << " ";
}

void print_node(const xmlpp::Node* node, unsigned int indentation = 0)
{
  std::cout << std::endl; //Separate nodes by an empty line.

  const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
  const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
  const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);

  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;
  }

  //Treat the various node types differently:
  if(nodeText)
  {
    print_indentation(indentation);
    std::cout << "text = \"" << nodeText->get_content() << "\"" << std::endl;
  }
  else if(nodeComment)
  {
    print_indentation(indentation);
    std::cout << "comment = " << nodeComment->get_content() << std::endl;
  }
  else if(nodeContent)
  {
    print_indentation(indentation);
    std::cout << "content = " << nodeContent->get_content() << std::endl;
  }
  else if(const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node))
  {
    //A normal Element node:

    //line() works only for ElementNodes.
    print_indentation(indentation);
    std::cout << "     line = " << node->get_line() << std::endl;

    //Print attributes:
    const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
    for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
    {
      const xmlpp::Attribute* attribute = *iter;
      print_indentation(indentation);
      std::cout << "  Attribute " << attribute->get_name() << " = " << attribute->get_value() << std::endl;
    }

    const xmlpp::Attribute* attribute = nodeElement->get_attribute("title");
    if(attribute)
    {
      std::cout << "title found: =" << attribute->get_value() << 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" прим. пер.) будут вызваны, когда
   документ проанализирован.


Пример

   Этот  пример  показывает,  как  методы  указателя  вызываются во время
   анализа.

   Source Code: http://libxmlplusplus.sourceforge.net/examples/sax_parser
   File: myparser.h



#ifndef __LIBXMLPP_EXAMPLES_MYPARSER_H
#define __LIBXMLPP_EXAMPLES_MYPARSER_H

#include <libxml++/libxml++.h>

class MySaxParser : public xmlpp::SaxParser
{
public:
  MySaxParser();
  virtual ~MySaxParser();

protected:
  //overrides:
  virtual void on_start_document();
  virtual void on_end_document();
  virtual void on_start_element(const Glib::ustring& name,
                                const AttributeList& properties);
  virtual void on_end_element(const Glib::ustring& name);
  virtual void on_characters(const Glib::ustring& characters);
  virtual void on_comment(const Glib::ustring& text);
  virtual void on_warning(const Glib::ustring& text);
  virtual void on_error(const Glib::ustring& text);
  virtual void on_fatal_error(const Glib::ustring& text);
};

#endif //__LIBXMLPP_EXAMPLES_MYPARSER_H



   File: main.cc



#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <fstream>
#include <iostream>

#include "myparser.h"

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;

    std::ifstream is(filepath.c_str());
    char buffer[64];

    MySaxParser parser;
    do {
      is.read(buffer, 63);
      Glib::ustring input(buffer, is.gcount());

      parser.parse_chunk(input);
    }
    while(is);

    parser.finish_chunk_parsing();
  }

  return 0;
}



   File: myparser.cc



#include "myparser.h"

#include <iostream>

MySaxParser::MySaxParser()
  : xmlpp::SaxParser()
{
}

MySaxParser::~MySaxParser()
{
}

void MySaxParser::on_start_document()
{
  std::cout << "on_start_document()" << std::endl;
}

void MySaxParser::on_end_document()
{
  std::cout << "on_end_document()" << std::endl;
}

void MySaxParser::on_start_element(const Glib::ustring& name,
                                   const AttributeList& attributes)
{
  std::cout << "node name=" << name << std::endl;

  // Print attributes:
  for(xmlpp::SaxParser::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
  {
    std::cout << "  Attribute " << iter->name << " = " << iter->value << std::endl;
  }
}

void MySaxParser::on_end_element(const Glib::ustring& name)
{
  std::cout << "on_end_element()" << std::endl;
}

void MySaxParser::on_characters(const Glib::ustring& text)
{
  std::cout << "on_characters(): " << text << std::endl;
}

void MySaxParser::on_comment(const Glib::ustring& text)
{
  std::cout << "on_comment(): " << text << std::endl;
}

void MySaxParser::on_warning(const Glib::ustring& text)
{
  std::cout << "on_warning(): " << text << std::endl;
}

void MySaxParser::on_error(const Glib::ustring& text)
{
  std::cout << "on_error(): " << text << std::endl;
}

void MySaxParser::on_fatal_error(const Glib::ustring& text)
{
  std::cout << "on_fatal_error(): " << text << std::endl;
}



TextReader парсер


   Подобно   парсеру   SAX,   парсер  TextReader  подходит  для  пакетной
   обработки,  но  вместо  использования указателей на определенные части
   документа, он позволяет определять тип текущей ветви, обработать ветвь
   соответствующим  образом  и  пропустить  в  документе столько, сколько
   необходимо. Не так, как при использовании парсера DOM, вы не сможете в
   XML-документе  вернуться  назад.  С  другой  стороны,  у  вас не будет
   расточительной  по времени обработки ветвей, которые вам не нужны, что
   наблюдается при использовании парсера SAX.

   Все  методы  находятся в одном объекте синтаксического анализатора, но
   их   результат  зависит  от  данной  ситуации.  Например,  используйте
   read()для перемещения к следующей текстовой ветви, а move_to_element()
   для  навигации  по  дочерним ветвям. Эти методы возвращают false, если
   доступных   узлов  больше  нет.  Тогда  используйте  один  из  методов
   get_name() и get_value() для проверки элементов и их атрибутов.

Пример

   Этот  пример проверяет каждый узел по очереди, а затем перемещается на
   следующий.

   Source Code: http://libxmlplusplus.sourceforge.net/examples/textreader
   File: main.cc


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <libxml++/libxml++.h>
#include <libxml++/parsers/textreader.h>

#include <iostream>

struct indent {
  int depth_;
  indent(int depth): depth_(depth) {};
};

std::ostream & operator<<(std::ostream & o, indent const & in)
{
  for(int i = 0; i != in.depth_; ++i)
  {
    o << "  ";
  }
  return o;
}

 int
main(int argc, char* argv[])
{
  try
  {
    xmlpp::TextReader reader("example.xml");

    while(reader.read())
    {
      int depth = reader.get_depth();
      std::cout << indent(depth) << "--- node ---" << std::endl;
      std::cout << indent(depth) << "name: " << reader.get_name() << std::endl;
      std::cout << indent(depth) << "depth: " << reader.get_depth() << std::endl;

      if(reader.has_attributes())
      {
        std::cout << indent(depth) << "attributes: " << std::endl;
        reader.move_to_first_attribute();
        do
        {
          std::cout << indent(depth) << "  " << reader.get_name() << ": " << reader.get_value() << std::endl;
        } while(reader.move_to_next_attribute());
        reader.move_to_element();
      }
      else
      {
        std::cout << indent(depth) << "no attributes" << std::endl;
      }

      if(reader.has_value())
        std::cout << indent(depth) << "value: '" << reader.get_value() << "'" << std::endl;
      else
        std::cout << indent(depth) << "novalue" << std::endl;

    }
  }
  catch(const std::exception& e)
  {
    std::cout << "Exception caught: " << e.what() << std::endl;
  }
}




<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>

Обсуждение [ RSS ]
  • 1.1, Седышев Витольд (?), 14:34, 20/02/2008 [ответить]  
  • +/
    Огромное спасибо Гришкову Никите за данную статью. Пробежал по статье - очень доходчиво описаны как DOM, так и SAX технологии разбора XML документа. Проработую на неделе весь материал. Долго искал что-то похожее для Windows, но в MSDN нашел только COM-парсер (тот который MSXML), ИМХО, неудобный он какой-то.
     
     
  • 2.3, kotfantazer (ok), 17:33, 22/02/2008 [^] [^^] [^^^] [ответить]  
  • +/
    >в MSDN нашел только COM-парсер (тот который MSXML), ИМХО, неудобный он какой-то.

    Может быть, виной тому префикс MS ? :D

     
     
  • 3.7, Vitold S (?), 16:15, 20/03/2011 [^] [^^] [^^^] [ответить]  
  • +/
    >>в MSDN нашел только COM-парсер (тот который MSXML), ИМХО, неудобный он какой-то.
    > Может быть, виной тому префикс MS ? :D

    Не скорее всего проблема не в MS, а в том что он написан с использованием технологии COM.

     

  • 1.4, Евгени (?), 22:28, 19/07/2009 [ответить]  
  • +/
    Привет всем, использую в своей программе DomParser.

    Столкнулся с такой проблемкой.

    Создаю строчку и передаю её parser.parse_memory_raw(...)

    Если строка содержит правильный XML документ все работает хорошо, или ошибка в документе, все перехватывается по try {}catch(const xmlpp::exception& ex) {

    Но когда передею в строчке произвольный набор символом на этапе парсинга, не генерируется исключение, а программы завершает свою работу выдавая результат об ошибке парсинга в консоль. Как избежать этого, как сделать так чтоб программа не закрывалась а можно было в ней обработать результат и продолжыть выполнение кода дальше?

     
  • 1.5, fasd (?), 12:12, 29/04/2010 [ответить]  
  • +/
    Блин а нельзя было что-нибудь новое написать?
    Например показать как с разделами cdata работать!
    Весь интернет блин кишит этими ксероксами которые врядли чтото кроме копипасты сделать могут
     
     
  • 2.6, kotfantazer (ok), 13:32, 30/04/2010 [^] [^^] [^^^] [ответить]  
  • +/
    >Блин а нельзя было что-нибудь новое написать?
    >Например показать как с разделами cdata работать!
    >Весь интернет блин кишит этими ксероксами которые врядли чтото кроме копипасты сделать
    >могут

    :) Да я в свое время тоже маялся. В итоге перевел (как смог) эту статью.
    Вообще ничего по-русски не было. Уж ежели не нравится - либо по-английски изучайте, либо переводите сами :)

     

    игнорирование участников | лог модерирования

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру