next_inactive up previous


CASL: XML API: Руководство Программиста
DocumentId:GradSof-CASL-XML-r-PG-26.12.2001-1.0.0

Введение

Одним из стандартных сервисов CASL является сервис XML API. Используя этот сервис прикладной программист может использовать функциональность разбора и обработки XML в своих приложениях.

Что конкретно входит в это XML API:

Основные понятия

Что такое потоковый (или управляемый событиями) парсер: это модуль, который читает элементы XML и генерирует события (т. е. вызывает соответствующие функции клиента парсера) по мере чтения XML потока.

Исходя из этого мы можем определить соответствующие структуры данных.

Классы

Очень высокоуровневое представление дает следующая диаграмма классов: png

Точное описание API привадится в справочнике по API, этот документ играет дополнительную роль.

XMLTransformer

Наконец XMLTransformer - собственно из-за чего все это и затевалось. Как видно из UML диаграммы, XMLTransformer преобразовывает входной поток XML событий в выходной.

Вход XMLTransformer задается с помощью метода setXMLSource(XMLSource* src,bool srcOwnity),

Для каждого XML события Xxx определен виртульный метод processXxx, с обычным набором параметров.

Каков алгоритм XML процессинга: для каждого события вызывается соответствующая функция трансформатора. Если она возворащает true, то событие передается приемнику, иначе - пропускается.

Клиент перегружает в своем классе, наследуемом от XMLTramsformer, соответствующие функции обработки processXxx. В этих функциях обработки он может использовать методы нотификатора sendXXX, для передачи сообщений дальше (к конечному приемнику).

Так как XMLTransformer реализует интерфейсы XMLSource и XMLDostination, то мы можем выстраивать цепочки преобразователей, как на следующей диаграмме взаимодействия:

XML_Events.png

Для выстраивания таких цепочек предназначены методы chainBefore и chainAfter

Пример XML трансформации

К примеру, напишем XML трансформатор, который преобразовывает инструкции преобразования типа <?include-file fname ?> в содержимое файла fname

/*
 * this program illustrate how to build own processing instructions handlers.
 * Level 0:
 *   handle XML process instruction <?include fname >
 * change during processing to content of file (as CharData) or to  
 * processing instruction <?error-opening-file fname>
 */

using namespace CASL;
using namespace std;

/**
 * So, we derive our transformer from XML transformer.
 **/
class PIIncludeHandler: public XMLTransformer
{
public:

  /**
   * and overload method 'processPI'
   **/
  bool processPI(WStringChunk& piName, WStringChunk& piBody, XMLContext& ctx);

};

/**
 *@param piName - name of processing instruction. (must be 'include')
 *@param piBody - body of processing instruction. in our case: name of
 * file, may-be surrounded by extra whitespaces.
 *@return true, if we does not handle this instruction, and what to pass it
 *   to next handlers in chain, otherwise 
 *   (i. e. if this is our instruction) - false.
 **/
bool PIIncludeHandler::processPI(WStringChunk& piName, 
                                 WStringChunk& piBody, 
                                 XMLContext& ctx)
{
 cerr << "processPI" << endl;
 std::string sPIName = CharConventer::toEncoding(piName,"ASCII").to_string();
 cerr << "piName=" << sPIName << endl;
 if (piName.equalASCII("include")) { // we must use ASCII name.
   string fname = Utils::toASCII(piBody); 
    // delete extra spaces.
   int wspos=fname.find_first_not_of(" \t\r\n");
   fname=fname.substr(wspos, fname.find_last_not_of(" \t\r\n")-wspos+1);
   std::ifstream input(fname.c_str()); // try to open file.
   if (!input) {
          // create new PI 'error-opening-file'
     WStringChunk newPI=Utils::fromASCII("error-opening-file");
          // send one.
     sendPI(newPI,piBody,ctx);
   }else{
     // copy all as sequence of CharData items.
     while(!input.eof()) {
        string buff;
        input >> buff;
        if (!input.eof()) buff+="\n";
        WStringChunk content = CharConventer::toUtf16(buff,"utf8");
        sendCharData(content,ctx);
     }
     input.close();
   }
   // As we handle this instruction, does not pass it up.
   return false;
 } else {
   // this is not our instruction - so simple return true.
   return true;
 }
}

Этот пример находится в директории demo/XML/PIInclude исходного дистрибутива CASL.

При разборе примера советуем обратить внимание на использование утилит и классов преобразования символов: соответствующее описание API: Utils и CharConventer

XMLService

Хорошо, мы создали преобразователь, теперь как его применить ?

CASL предоставляет нам статические методы класса XMLService API

Вот фрагмент кода, запускающий наш преобразователь:

01 try {
02  ifstream input(inputFileName.c_str()); 
03 if (!input) {
04    errors() << "Can't open file " << inputFileName << 
05                " for reading!" << endl;
06    return;
07 }
08 ofstream output(outputFileName.c_str());
09 if (!output) {
10    errors() << "Can't open file " << outputFileName << 
11                " for writing!" << endl;
12    return;
13 }
14 // do transformation:
15 //   we can cath errors in hight-level block
16 // create transformer.
17 auto_ptr<XMLTransformer> tr(XMLService::createXMLTransformer(input,output));
18
19 // and chain our transformer into.
20 PIInclude0Handler myTransformer;
21 tr->chainAfter(myTransformer,false);
22
23 // now create context and run all.
24 XMLContext ctx;
25 tr->process(ctx);
26
27 // That's all
28 input.close();
29 output.close();
30 }catch(const XMLException& ex){
31  errors() << ex.what() << endl;
32 }

Обратите внимание на строчки с 14 по 25 : мы создаем XMLTransformer, связанный со входным потоком input и выходным потоком output. После этого добавляем в цепочку наш трансформер, создаем XMLContext и запускаем процесс разбора.

XMLContext

Теперь обратим внимание на класс XMLContext. API Экземпляры этого класса используются для передачи информации между различными стадиями XML процессинга.

XMLContext::Slot

Сам XMLContext это последвовательность слотов. Что такое XMLContext::Slot - абстрактный класс, полностью определяемый пользователем.

class XMLContext : public Scriptable
{
public:

  class Slot: public Scriptable
  {
   public:
     Slot();
     virtual ~Slot();
     virtual ValueProxy doCommand(const char* command, Value* params);
   ......
  };
 
  .......
};

Перечень изменений

  1. 03.07.2002 - первая редакция.
  2. 26.12.2001 - создан.



Footnotes

... std::istream).1
Это требование не ограничивает общности, так как потоки данных, получаемые из сети также могут быть оформленны в виде специализированных потоков C++

next_inactive up previous
GradSoft