Programming xml with c#

17 ноября 1999 года Фабио Арчиниегас А. C++ — это популярный язык программирования. Для которого уже существует множество проектов. Связанных с XML. Цель этой статьи-представить и проанализировать различные варианты. Доступные при использовании C++ для ваших XML-приложений. Мы рассмотрим две вещи: основные API и стратегии для анализа и манипулирования XML в вашем приложении C++. А также практическое использование и компромиссы подходов к анализу XML.

Чтобы получить максимальную отдачу от этой статьи. Требуется базовое понимание языка С++.

Диаграммы статических моделей иллюстрируются на языке UML: используемые диаграммы показывают в основном наследование и простые отношения и могут не требовать предварительного знания языка UML. Тем не менее. Мы предоставляем базовое руководство по UML, содержащее все. Что вам нужно знать. Чтобы понять примеры.

Различные подходы к обработке XML

Было создано несколько наборов инструментов и библиотек для манипуляций на основе C++. Эти наборы инструментов в основном делятся на две категории: процессоры. Управляемые событиями. И процессоры построения объектных моделей.

Мы рассмотрим и то, и другое.

Событийные подходы

При событийном подходе к обработке XML-данных синтаксический анализатор считывает данные и уведомляет специализированные обработчики, которые выполняют необходимые действия. Обратите внимание. Что здесь термин event-driven означает процесс. Который вызывает определенные обработчики при обнаружении содержимого XML-документа.

Например, вызов endDocument() при обнаружении конца XML — документа.

Различные реализации синтаксического анализатора XML различаются своими интерфейсами прикладных программ. Например. Один синтаксический анализатор может уведомить обработчик о начале элемента. Передавая ему только имя элемента. А затем требуя другого вызова для обработки атрибутов. Другой парсер может уведомить обработчик. Когда он находит тот же тег start-element. И передать его не только имя элемента. Но список атрибутов и значений этого элемента.

Еще одно важное различие между XML-парсерами заключается в том. Какое представление они используют для передачи данных из парсера в приложение: например. Один парсер может использовать список строк STL. В то время как другой может использовать специально созданный класс для хранения атрибутов и значений.

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

// Пример: различные способы передачи данных обработчикам // Передача атрибутов на основе STL // с помощью событийно-управляемого обработчика на основе STL startElementHandler // метод может выглядеть следующим образом virtual void HypotheticalHandler::startElementHandler (const String name, attributes) = 0; // Предоставлен специальный класс списка атрибутов // С некоторыми event-управляемыми API-интерфейсами специальный объект AttributeList // содержащий атрибутивную информацию // используется. Именно так обстоит дело с парсером IBM xml4c2. virtual void DocumentHandler::startElement(const XMLCh* const name, AttributeList& attrs) = 0; 

Как вы можете видеть, способ. Которым процессоры уведомляют приложения об элементах, атрибутах. Символьных данных. Инструкциях обработки и сущностях. Специфичен для синтаксического анализа и может сильно влиять на стиль программирования. Лежащий в основе связанных с XML модулей вашей системы.

Усилия по созданию стандартного API обработки XML. Управляемого событиями. Привели к созданию SAX (Simple API for XML). Стандартный интерфейс для SAX в C++ еще не разработан. Тем не менее. Важность и растущее использование SAX в приложениях на основе C++ XML не вызывает сомнений и делает его важной темой нашего обсуждения.

В следующих двух разделах мы рассмотрим идеи. Лежащие в основе как несаксового. Так и основанного на саксофоне событийного подхода к анализу. Для наших примеров мы будем использовать expatpp (C++ — оболочку парсера expat Джеймса Кларка) и xml4c2 (XML-парсер IBM C++) соответственно. Парсер IBM будет переиздан в конце этого года как проекта Apache XML.

Не SAX event-driven подходы

Expat-это парсер языка Си. Разработанный и поддерживаемый Джеймсом Кларком. Он управляется событиями в том смысле. Что вызывает обработчики по мере того. Как части документа обнаруживаются синтаксическим анализатором. Определяемые пользователем функции могут быть зарегистрированы как обработчики.

Вот пример типичного использования экспата в языке Си:

/* Это простая демонстрация того. Как использовать expat. Эта программа считывает XML - документ из стандартного ввода и записывает строку с именем каждого элемента для стандартного вывода. Отступом дочерние элементы на одну вкладку останавливаются больше. Чем их родительский элемент. [Взято из стандартного распределения экспатов] */ #включить void startElement (void *userData. Const char *name. Const char **atts) { int i; int *depthPtr = userData; для (i = 0; i) путчар('\t'); ставит(ставит(имя); void endElement(void *userData. Const char *name) { int *depthPtr = userData; int main() { char buf[BUFSIZ]; XML_Parser parser = XML_ParserCreate(NULL); инт сделано; int depth = 0; XML_SetUserData(parser, &depth); XML_SetElementHandler(парсер, startElement, endElement); делать { size_t len = fread(buf, 1, sizeof(buf). Stdin); готово = лен); if (!XML_Parse(parser, buf, len. Done)) { fprintf(stderr. XML_ErrorString(XML_GetErrorCode(парсер)). XML_GetCurrentLineNumber(парсер)); } while (!done); XML_ParserFree(парсер); 

Эта программа просто показывает строку Обратите внимание на существование параметра void *userData, который expat использует . Чтобы дать вам возможность управлять вашей информацией между вызовами. В предыдущем примере userData используется для отслеживания уровня отступа. Который должен использоваться при печати элементов и атрибутов в стандартном выводе.

Expat имеет много преимуществ: он очень быстрый и очень портативный. Он также находится под GPL (GNU General Public License). Что означает. Что вы можете свободно использовать и распространять его. Но это просто C. Поэтому необходимо выбрать какую-то стратегию. Чтобы интегрировать ее с вашим проектом OO C++.

Одной из стратегий было бы просто создать глобальные функции для регистрации в expat. Эти функции могут получить указатель на данные. Которые вы хотите изменить во время чтения файла (например. Объект Count. Который будет хранить количество символов в файле). А затем все. Что вам нужно сделать. Это зарегистрировать их в expat. Это прямолинейный подход. Но он привносит в картину несколько нежелательных последствий:

  • Это уменьшает модульность вашей программы.

  • Это делает вашу программу менее сплоченной (т. е. связанные методы не связаны друг с другом).

  • Это может разрушить ваш дизайн OO на фундаментальном уровне (например. XML-сериализация ваших объектов).

Все вышеперечисленное, вероятно. Приведет к менее ремонтопригодной программе с подверженным ошибкам дизайном. Лучшим вариантом было бы обернуть expat с помощью класса C++. Который инкапсулирует детали C и предоставит вам чистый список методов. Которые вы можете переопределить в соответствии с вашими конкретными потребностями. Вот как работают обертки вроде expatpp.

Expatpp-это оболочка C++ для expat. Он был разработан Энди Дентом с этой основной идеей: конструктор expatpp создает экземпляр парсера expat и регистрирует фиктивные функции в качестве обработчиков. Вызывающих соответствующие методы. Способные переопределять expatpp.

Какой-то код сделает вещи более понятными:

// В своем определении класса expatpp объявляет обратные вызовы // что он будет регистрироваться в парсере: static void startElementCallback (void *userData. Const XML_Char* имя. Const XML_Char** atts); static void endElementCallback (void *userData. Const XML_Char* name); static void charDataCallback(void *userData. Const XML_Char* s. Инт лен); //... и так далее для других обработчиков. Таких как // processingInstructionCallback // В конструкторе expatpp создает парсер expat и регистрирует его // обратные вызовы expatpp::expatpp() { mParser = XML_ParserCreate(0); XML_SetUserData(mParser. This); //Обратите внимание. Что пользовательские данные //сам объект XML_SetElementHandler(mParser. StartElementCallback. EndElementCallback); XML_SetCharacterDataHandler(mParser. CharDataCallback); // Теперь для каждого обратного вызова есть партнер. Способный переопределять член // как virtual void startElement(const XML_Char* name. Const XML_Char** atts){ // Обратите внимание. Что поведение по умолчанию-ничего не делать. В вашем // производный класс вы можете переопределить это и, например. Распечатать // имя элемента типа 

Как вы можете видеть. UserData используется для поддержания указателя на ваш объект expatpp. Когда объект построен. Обратные вызовы регистрируются как обработчики для expat. Когда происходят события синтаксического анализа. Обработчики вызывают соответствующие методы в классе. Поведение по умолчанию этих методов состоит в том. Чтобы ничего не делать. Но вы можете переопределить их для своих собственных целей.

интерфейс expatpp

Интерфейс expatpp определяет оболочки для всех методов в expat и включает в себя следующие элементы:

 виртуальная пустота startElement (const XML_Char* name. Const XML_Char** atts); виртуальный void endElement(const XML_Char* name); виртуальная пустота charData(const XML_Char *s. Int len); virtual void ProcessingInstruction(const XML_Char* target. Const XML_Char* данные); virtual void DefaultHandler(const XML_Char *s. Int len); виртуальная пустота unparsedEntityDecl(const XML_Char *EntityName. Const XML_Char* base. Const XML_Char* SystemID. Const XML_Char* publicId. Const XML_Char* notationName); virtual void notationDecl(const XML_Char* notationName. Const XML_Char* base. Const XML_Char* SystemID. Const XML_Char* publicId); // XML-интерфейсы int XMLPARSEAPI XML_Parse (const char *s. Int len. Int isFinal); XML_Error XMLPARSEAPI XML_GetErrorCode(); int XMLPARSEAPI XML_GetCurrentLineNumber(); 

Этот интерфейс определяет базу обработчиков для expatpp (подробнее см. исходный код). Наряду с приведенным ниже примером этого должно быть достаточно. Чтобы начать работу с expatpp в XML-проекте.

образец expatpp

В следующем примере expatpp используется для создания древовидного представления элементов документа. В отличие от остальных примеров в этой статье. Эта конкретная программа была построена с использованием Inprise C++ builder и зависит от него.

 // Мы объявляем обработчик. Который будет способен // построение дерева. Для того чтобы сделать это он будет переопределять // методы startElement и endElement (остальные могут // быть проигнорированным) // [из MyParser.h] класс MyParser : public dexpatpp { Частное: TTreeView *mTreeView; // Древовидное представление. В котором будут показаны элементы TTreeNode *lastNode; Публично: встроенный MyParser(TTreeView *treeToUse); inline void startElement (const XML_Char* name. Const XML_Char** atts); // Теперь. Для реализации, все. Что нам нужно сделать. Это встроенный void MyParser::startElement (const XML_Char* name. Const XML_Char** atts) { // и встроенный void MyParser::endElement(const XML_Char* name) { 

Для получения полного и более подробно документированного кода загрузите этот файл: expatppExample.zip. Когда вам будет предоставлен XML-файл. Приложение выдаст что-то вроде этого:

Тот же подход может быть использован и использовался для обертывания других синтаксических анализаторов C для использования в коде C++. Эти другие парсеры включают. В частности. Парсер проекта Gnome libxml Даниэля Вейяра.

Следующий раздел будет посвящен простому API для XML—SAX.

SAX событийные подходы

Простой API для XML, SAX, — это управляемый событиями API для анализа XML-документов. Он определяет несколько классов обработчиков. Которые инкапсулируют методы. Необходимые для конкретных задач при разборе XML-документов. Таких как обработка внешних сущностей. Как и в случае с другими парсерами. Управляемыми событиями. Основной процесс определения XML-модуля в вашем проекте может быть описан следующими шагами:

  • Подкласс необходимых базовых классов обработчиков. (В предыдущем разделе вы делали это только из expatpp; теперь у вас есть больше классов. Доступных для подклассов. Которые мы рассмотрим ниже.)

  • Переопределите нужные методы.

  • Зарегистрируйте свой обработчик в парсере.

  • Начните разбор.

Эти шаги можно увидеть в следующем примере. Который печатает XML-файл. Отслеживая правильный отступ. Используемые интерфейсы описаны ниже. Вы также можете посмотреть пример SAXCount из документации IBM xml4c2.

// Мы объявляем собственный обработчик. Который будет способен // запоминание правильного отступа для слова // печать файла. Для этого мы переопределяем // обработчики startElement. Characters и endElement. // Потратьте время. Чтобы сравнить это решение с решением expatpp. // та же проблема (выше) void PrettyPrint::startElement(const XMLCh* const name. Список атрибутов и атрибуты AttributeList& attributes) { indent++; // Запущен новый элемент. Он должен быть с отступом один // уровень дальше текущего уровня int i; for(i = 0; i) outStrm 

Скачать полный исходный код можно здесь: saxExample.zip.

Что делает SAX важным. Так это не идея. Лежащая в основе синтаксического анализа—суть событийно-ориентированного подхода та же. Что и у expatpp или любого другого событийно-ориентированного парсера,—а стандартизация интерфейсов и классов. Используемых для связи с приложением во время процесса синтаксического анализа.

Эти классы и интерфейсы (абстрактные классы в C++) делятся таким образом:

  • Классы, реализуемые парсером: Parser, AttributeList. Locator (необязательный класс. Используемый для отслеживания местоположения события)

  • Классы, реализуемые приложением: DocumentHandler (очень важно—это тот, который вы будете подклассировать почти во всех приложениях), ErrorHandler, DTDHandler. EntityResolver.

  • Стандартные классы SAX: InputSource. SAXException. SAXParseException и HandlerBase. (Это может быть вашей отправной точкой во многих приложениях. Поскольку он наследуется от всех обработчиков. Обеспечивая поведение по умолчанию для всех не переопределенных методов.)

SAX изначально разрабатывался для Java. Но был портирован на другие языки. Такие как Python. Perl и C++. В C++ у вас есть несколько представлений и стратегий на выбор при переносе исходного SAX API. Поскольку общего интерфейса C++ SAXне существует . Различные реализации могут иметь некоторые небольшие и не очень маленькие различия.

В этой статье мы остановимся на реализации IBM xml4c2 SAX. Чтобы написать свои собственные XML-модули. Вам нужно будет наследовать от классов приложений API и переопределять методы. Которые вы хотите выполнять специальные действия.

Вот обзор обработчиков. От которых вы унаследуете. Более полную документацию о них можно найти в дистрибутивеxml4c2 .

Обработчик Описание
ДокументОоборот Это основной интерфейс. Который реализуют приложения SAX. Он определяет методы. Позволяющие парсеру информировать приложение об основных событиях синтаксического анализа. Чтобы использовать его. Приложение должно использовать класс. Реализующий DocumentHandler. А затем зарегистрировать экземпляр в парсере. Который позже будет передавать ему соответствующие события.
ErrorHandler Этот интерфейс предоставляется для того. Чтобы приложение SAX могло реализовать индивидуальную обработку ошибок. Он регистрируется с помощью метода setErrorHandler. Затем парсер будет сообщать обо всех ошибках и предупреждениях через этот интерфейс.
DTDHandler Объекты класса. Реализующего интерфейс DTDHandler. Получают информацию о нотациях и несвязанных сущностях. Они регистрируются с помощью метода setDTDHandler парсера.
EntityResolver (реже используется) Если приложению необходимо перехватить какие-либо внешние объекты перед их включением. Оно должно использовать класс. Реализующий этот интерфейс. Регистрирующий его с помощью метода setEntityResolver. Любые внешние объекты (включая внешнее подмножество DTD и внешние объекты параметров) будут сообщаться через него.

Обратите внимание. Что. Используя поддержку множественного наследования в C++. Пользовательский обработчик может реализовать несколько из этих функций (например. Обработку ошибок и обработку документов).

 // ... класс MyHandler : public DocumentHandler. ErrorHandler // ... parser = новый NonValidatingSAXParser; MyHandler* handler = новый MyHandler(); 

XML-часть вашего приложения, вероятно. Примет следующую форму:

Если вы знакомы с шаблонами, вы увидите. Что это похоже на простой шаблон компоновщика, т. Е. Мы отделяем ответственность XML от клиентских объектов и делегируем ее коллекции объектов (самому парсеру и вашим обработчикам). Которые будут знать. Как постепенно построить некоторый продукт. Полное описание шаблона строителя см. в книге (

Обратите внимание. Что этот продукт может быть выражен как другой объект. Простое возвращаемое значение или даже как некоторая трансформация атрибутов объекта обработчика.

Это завершает обзор SAX и может служить отправной точкой для ваших XML-модулей C++. Пожалуйста, ознакомьтесь с документацией выбранной вами реализации для получения дополнительных примеров. IBM xml4c2 рекомендуется использовать из-за его обширной документации.

Подходы к объектной модели

В предыдущем разделе был представлен событийный подход к обработке XML-документов. Существует еще один вариант обработки XML-документов: подход Этот подход также известен как

Существует стандартная независимая от языка спецификация. Написанная на IDL OMG. Для построенной объектной модели. Она называется объектной моделью документа, или DOM.

ДОМ

В следующем разделе представлены основные идеи. Лежащие в основе DOM. И типичные шаги. Связанные с написанием модуля на основе XML DOM в вашем приложении C++.

Выражение документа как структуры и предоставление его приложению не является чем-то новым: все основные браузеры делали это в течение многих лет своим собственным фирменным способом. Важная идея XML DOM заключается в том. Что он стандартизирует модель. Используемая при представлении любого XML-документа в памяти. Парсеры C++ на основе DOM создают DOM-представление документа вместо того. Чтобы информировать приложение. Когда они сталкиваются с элементами. Атрибутами и т. Д.

Объектная модель документа — это независимый от языка и платформы интерфейс. Который позволяет программам и сценариям динамически получать доступ и обновлять структуру содержимого и стиль документов. Существует основной набор интерфейсов. Которые должна обеспечивать каждая реализация. Совместимая с DOM 1.0. Здесь мы сосредоточимся на этих основных интерфейсах. В настоящее время все. Что находится в документе. Может быть доступно с помощью DOM (1.0). За исключением внутреннего и внешнего DTD-подмножества. Для которых в настоящее время не существует API.

DOM, как следует из названия. Является объектной моделью, а не моделью данных.

Объектно-ориентированные интерфейсы определяют семантику структурной модели независимо от выбранной для нее реализации. Это означает. Что реализации парсера DOM могут свободно выбирать любое внутреннее представление. Которое им нравится. При условии. Что они соответствуют интерфейсам DOM. В следующем разделе будут показаны основные интерфейсы ядра DOM; затем мы рассмотрим шаги. Которые вы будете использовать с подходом DOM.

Интерфейсы DOM

Ядро уровня 1 DOM определяет базовый набор интерфейсов. Которые позволяют манипулировать XML-документами. Он предоставляет методы доступа к документу и его заполнения. Эти методы инкапсулируются в два набора интерфейсов: фундаментальные основные интерфейсы и расширенные интерфейсы.

Вот основное представление основных интерфейсов. Для полного описания и всех методов вам нужно будет скачать библиотеку DOM. Опять же, xml4c2-хороший выбор из-за его отличной документации.

Фундаментальные интерфейсы

Интерфейс Описание
Узел Этот интерфейс является основным типом данных для всей объектной модели документа. Он представляет собой один узел в дереве документов. Это базовый интерфейс для всего в модели—поэтому все объекты. Реализующие интерфейс узла. Предоставляют определенные им методы. Следует быть осторожным в этом отношении. Потому что некоторые производные узла. Такие как текстовый узел. Выставляют некоторые Методы узла. Которые они действительно не поддерживают, такие как
Документ Класс для ссылки на узлы XML-документа в DOM. Концептуально узел документа DOM является корнем дерева документов и обеспечивает первичный доступ к данным документа.
Фрагмент документа DocumentFragment-это Этот объект инкапсулирует часть документа. Что очень полезно в приложениях. Которым нужно переставить или изменить части дерева. Например редактор. Выполняющий вырезание/вставку. Обратите внимание. Что содержащийся фрагмент не является (обязательно или даже часто) допустимым XML-документом.
Элемент

Большинство объектов. Помимо текста. Которые можно найти в дереве DOM. Являются узлами элементов DOM. Они представляют элементы в объектной модели документа. И поскольку они могут иметь другие узлы элементов в качестве дочерних элементов. Их структура отражает расположение исходного XML-документа.

Другие фундаментальные интерфейсы

Остальные основные интерфейсы: DOMImplementation, NodeList. NamedNodeMap. CharacterData. DOMException (который в IDL является не интерфейсом. А исключением), Attr. Text, Comment. Опять же, для получения более подробной информации об этом вам рекомендуется загрузить полную документацию. Включенную в такие наборы инструментов. Как xml4c2.

Расширенные Интерфейсы

Расширенные интерфейсы также являются частью основного DOM. Эти интерфейсы являются:

Важные сведения о DOM level 1

Чтобы получить полное представление о DOM. Вам следует ознакомиться с рекомендацией W3C. Тем не менее. Вот несколько важных моментов. Которые следует иметь в виду:

Ограничения уровня 1

Уровень DOM 1 строго ограничен теми методами. Которые необходимы для представления структуры документа и управления ею. На момент написания этой статьи DOM level 2 еще не был одобрен в качестве рекомендации W3C. И никакой реализации C++ не было доступно. Поэтому ради удобства использования я решил сосредоточиться на DOM level 1. Будущие уровни DOM могут обеспечить:

  • Безопасность резьбы

  • Мероприятия

  • Управление рендерингом с помощью таблиц стилей

  • Контроль доступа

Упорство

Сохранение представления DOM остается на реализацию

Последствия изменений в узлах

Изменения в узле отражаются в любом NodeList или NamedNodeMap. Которые ссылаются на них. (Это означает использование ссылок или указателей в вашей реализации C++.)

Управление памятью

DOM API не зависит от философии управления памятью (т. е. рекомендация не определяет никакой политики управления памятью). Это ответственность за реализацию.

Тип DOMString

DOM определяет тип DOMString в IDL следующим образом


То есть последовательность 16-битных символов с использованием кодировки UTF-16.

Чувствительность к регистру

Несмотря на то. Что некоторые HTML-процессоры могут ожидать нормализации в верхнем регистре. DOM основывает свое соответствие строк исключительно с учетом регистра. Тем не менее. Рекомендация допускает. Что некоторые нормализации могут произойти до построения DOM.

Базовая структура модуля на основе XML DOM

В отличие от роли приложения во время анализа на основе событий. Фокус действия в приложении на основе DOM-это пост-синтаксический анализ. Базовая компоновка XML-модуля с использованием DOM может выглядеть примерно так:

Здесь основной класс XML-приложения больше не является обработчиком. А манипулятором создаваемого представления DOM. Обратите внимание. Что это важное изменение фокуса: обработка вашего приложения больше не выполняется во время синтаксического анализа. А скорее в дополнительном модуле. Который будет манипулировать узлами.

Основными шагами для создания вашего XML-модуля будут следующие:

  • Создание и регистрация обработчиков ошибок и других действий. Зависящих от реализации.

  • Создайте манипулятор DOM. Который будет отвечать за (1) выдачу запросов синтаксического анализа парсеру (2) манипулирование результатами таких запросов.

  • Включите необходимый манипулятор/остальную часть взаимодействия приложения.

Чтобы завершить картину и дать вам реальное представление о том. Как все это сочетается друг с другом. Лучше всего сделать полное прохождение примера реализации на основе DOM. Опять же, в качестве примера мы рассмотрим XML — Полный пример можно найти в образце DOMPrint. Распространяемом вместе с IBM xml4c2.

Обратите внимание. Что для больших проектов правильная инкапсуляция операций обработки DOM в приведенной выше схеме предназначена для поддержания чистоты вашего дизайна и управляемости вашей программой.

Перевод примера DOMPrint в DOM-манипулятор/DOM-Парсер/Остальную часть схемы приложения оставлен в качестве простого упражнения для читателя. В этом случае все. Что требуется. — это взять методы оригинала и инкапсулировать их в класс. Таким образом. Основная функция может быть чем-то вроде:

#включить static char* xmlFile = 0; // Имя файла для синтаксического анализа // -------------------------------------------------------------- // Main - очень упрощенная версия-только для целей руководства. // Обратите внимание. Что main больше не берет на себя ответственность за создание // парсер или печать файла. Попробуйте инкапсулировать это // в классе DOMBeautifier самостоятельно. На основе оригинала // DOM_Print (это очень просто и даст вам лучшее ощущение // из библиотеки) // -------------------------------------------------------------- int main(int argC. Char* argV[]) { // Проверьте командную строку и извлеките аргументы. если (argC != 2) возврат 1; // Первым параметром должно быть имя файла xmlFile = argV[1]; // Теперь инициализируйте платформу XML4C2 пробовать { catch(const XmlException& toCatch) { DomBeautifier *myBeautifier = новый DomBeautifier(); // Очистка удалить myBeautifier; 

Теперь, когда мы познакомились с основанными на событиях подходами и подходами DOM к обработке XML-документов на языке C++ . Мы рассмотрим различные соображения. Которые помогут вам выбрать между каждым подходом.

Использование и компромиссы

В этом разделе будет обсуждаться использование и компромиссы между подходами на основе событий и объектной модели. В частности. Будут рассмотрены три темы: наличие объектов с поддержкой XML. Которые могут использоваться в качестве обработчиков. Использование фабрик для создания/изменения объектов и наличие приложения. Ориентированного на документ. Раздел завершается списком синтаксических анализаторов C++ и их пригодностью для описанных целей.

Обратите внимание. Что проекты. Описанные здесь. Хотя и полезны и используются в реальных приложениях. Не обязательно являются лучшими для вашего проекта. Они предоставляются только в качестве руководства.

XML-зависимые объекты. Используемые в качестве обработчиков

Первая форма XML-модулей. Которые может использовать ваше приложение. — это простые классы с поддержкой XML. Предназначенные для регистрации в событийном синтаксическом анализаторе. Эти классы характеризуются не только своей обработчицей. Но и самоограничением своей деятельности. Их использование обычно не ставит под угрозу другие классы. И синтаксический анализ XML-документа часто приводит к изменению их атрибутов или операций вывода.

Эти классы будут принимать следующую простую форму:

Обратите внимание. Что база обработчиков. От которой наследуется ваш обработчик. Не обязательно является HandlerBase от SAX; мы используем этот термин. Потому что каждый событийно-управляемый синтаксический анализатор C++ будет предоставлять таким базовым классам поведение по умолчанию (HandlerBase в случае xml4c2 или SAXParser и сам expatpp в случае expatpp).

Многочисленные примеры такого подхода можно найти в каждом парсере. И они представляют собой простейшую. Хотя и очень полезную форму использования XML. Среди типичных примеров-подсчет элементов в документе и печать каждого элемента документа по мере его нахождения. Наш пример SAX pretty printing — хороший пример такого подхода.

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

Существуют ленивые способы создания DOM. Чтобы минимизировать потребление памяти. Но даже в этом случае в общих чертах событийный подход должен использоваться в этих случаях обхода ради простоты и хорошего использования ресурсов памяти.

Фабрики по созданию или модификации других объектов

Следующим типичным сценарием является определение фабричных классов для манипулирования другими объектами в соответствии с информацией в XML-документе. Эти классы обычно принимают следующую форму:

Первая из таких манипуляций. Которая приходит на ум. — сериализация. Процесс создания объекта постоянным с помощью XML — это очень важная проблема. Которую можно решить таким образом. Ответственность за сохранение объекта в виде XML также может лежать на самом объекте. Но этот подход—абстрагирование ответственности за сериализацию—часто является предпочтительным.

Можно найти много других применений этому подходу. Простым примером может быть манипулирование коллекцией объектов в соответствии с XML-кодированными инструкциями. Отправленными удаленным объектом, например. Коллекцией типа:

// Простой каталог STL с использованием ассоциативной карты контейнеров // Который будет содержать имена(ключи) и телефонные номера(длинные // которые служат значениями для этих ключей) // используется для сравнения двух ключей. 

может быть манипулирован удаленным объектом путем отправки таких сообщений, как


на наш завод. Который перевел бы их на:

 каталог[

Опять же, как и в более простых обработчиках. Описанных выше. Предпочтительным подходом для этих фабрик является событийный подход. Поскольку он не связан с затратами на хранение документа в памяти.

Конечно, есть много особых случаев. Которые необходимо учитывать. Возьмем случай с объектом. Который должен отвечать на вопросы других объектов о конкретном документе (например. В этом случае имеет место компромисс между потреблением памяти и скоростью. Поскольку можно использовать как DOM. Так и событийный подход. Первый включает в себя больше потребление памяти. Но значительно ускорит поиск конкретных элементов в документе. Второй не будет потреблять никакой памяти. Представляющей документ. Но потребует O(n) сложности по сравнению с XML-документом на диске для каждого поиска. При принятии окончательного решения необходимо будет учитывать количество запросов. Объем документов и объем имеющихся ресурсов.

До сих пор мы рассмотрели два в основном событийных использования. В следующем разделе мы рассмотрим случаи. Когда DOM является более естественным вариантом.

Приложения. Ориентированные на документы

Использование DOM может быть очень разнообразным, например. Выставление объектной модели документа в браузере. Чтобы можно было использовать язык расширений для манипулирования документом. Мы сосредоточимся на общем случае: использовании DOM в качестве модели в схеме Model-View-Controller.

Использование DOM в контроллере представления модели (MVC)

В основном MVC определяет структуру. В которой есть базовая модель. Несколько представлений. Отражающих состояние модели. И контроллер. Который получает запросы на изменение модели. Он также гарантирует. Что эти изменения будут сообщены представлениям.

Это имеет место во многих приложениях. Ориентированных на документы. Например, возьмите простое приложение. Которое читает документ. Содержащий такую заметку:


…и показывает его пользователю. Позволяя редактировать и записывать обратно в файл.

Это, как и любое другое приложение. Которое должно манипулировать объектным представлением самого документа в качестве базовой модели. Идеально подходит для использования DOM.

Можно использовать уточнения к модели построения дерева и модификации DOM с помощью прямых вызовов API. Объекты реального представления могут выдавать командные объекты. Включающие фрагмент документа. Являющийся целью изменения (вместо непосредственного изменения DOM). Кроме того, можно было бы применить несколько стратегий. Чтобы избежать загрузки всего документа сразу.

Тем не менее. Основная идея остается тем. Что пытается показать наш простой пример: всякий раз. Когда вам нужно манипулировать документом непосредственно как вашей моделью (вместо построенных предметно-специфичных объектов). DOM представляет собой естественный выбор.

Широко используемые парсеры

Мы закончим этот раздел, представив список часто используемых синтаксических анализаторов C++ и их основную информацию. (Для сравнения производительности, пожалуйста. Обратитесь к Сравнительному анализу XML-парсеров.)

Синтаксический анализатор Поддержка DOM Поддержка САКСОФОНА Поддерживаемые платформы Поддержка валидации Скачать с сайта
libxml ДА ДА Linux. Win32 (возможно. Многие другие) ДА ftp://rpmfind.net
экспат (C) НЕТ НЕТ Win32, Unix и практически везде современный компилятор C существует. НЕТ ftp://ftp.jclark.com
expatpp (только обертка. Без расширений) НЕТ НЕТ Win32, Linux. Mac (и многие другие непроверенные) НЕТ ftp://ftp.highway1.com
xml4c2 ДА ДА Платформы IBM: AIX 4.1.4 и выше, Win32 (компиляторы MSVC 5.0, 6.0), Solaris 2.6, HP-UX B10.2 (aCC и CC). HP-UX B11 (aCC и CC). Linux ДА www.alphaworks.ibm.com

Как вы можете видеть. C++ хорошо подходит для обработки XML с точки зрения доступности. Размера и сложности кода. Соответствия и даже в значительной степени переносимости. Не говоря уже о производительности. Нет ничего особенно лучшего в использовании других языков. Таких как Java. Для программирования XML.

Все языки имеют сильные и слабые стороны. Которые следует учитывать при выборе языка для вашего проекта. Возможно, правда. Что C++ имеет более крутую кривую обучения. И, возможно. Программистов на C++ найти немного сложнее. Но эти соображения не имеют ничего общего со способностью C++обрабатывать XML. Большую осторожность следует проявлять. Сталкиваясь с маркетинговой шумихой о якобы и специфическая технология. XML не является исключением.