По работе столкнулся с необходимостью использования xml-файлов в одном из разрабатываемых приложений. Выбор соответствующих библиотек велик, но так как разработка ведется в IDE CodeGear RAD Studio 2009, то не хотелось в очередной раз замарачиваться с подключением библиотек импорта (*.lib файла для компиляторов Borland и VS отличаются). К тому же задачи передо мной стояли тривиальные: создать xml-файл, добавить в него секцию, суметь прочитать из него данные. В итоге остановил свой взгляд на входящем в библиотеку VCL компоненте TXMLDocument. Данный же топик, по сути, - краткий обзор компонента, если угодно, введение в суть вопроса. Все, что я буду описывать, можно найти с лихвой во встроенной в IDE системе помощи. Но Все же мы иногда ленимся … ;=)
Вобщем, тем кто желает задействовать мощь xml-документов в своем приложении и при этом работает в Borland Developer Studio или CodeGear RAD Studio топик может показаться интересным.
Итак, приступим.
Допустим, имеется xml-файл следующего содержания:
<?xml version="1.0" encoding="UTF-8" ?>
<users>
- <user uid="1">
<name>Homer Simpson</name>
<comment>Отец семейства</comment>
</user>
- <user uid="2">
<name>Marge Simpson</name>
<comment>Красавица жена</comment>
</user>
- <user uid="3">
<name>Bart Simpson</name>
<comment>Малолетний озорник</comment>
</user>
- <user uid="4">
<name>Lisa Simpson</name>
<comment>Маленький гений</comment>
</user>
</users>
* This source code was highlighted with Source Code Highlighter.Рассмотрим, как :
- создать файл с такой структурой
- редактировать поля существующего файла
- добавлять новых пользователей
- считывать информацию из файла
Но прежде всего, пару вступительных слов о …
1.Создание объекта, инициализация и уничтожение:
В VCL имеется интерфейсный класс IXMLDocument, в котором и прописаны все методы для работы с XML-документами. Для языка С++ имя данного интерфейса заменяется на _di_IXMLDocument. Код в подтверждение тому:
typedef System::DelphiInterface<IXMLDocument> _di_IXMLDocument;
Компонент же TXMLDocument является ни чем иным, как реализацией интерфейса IXMLDocument.Существует несколько способов создать объект, реализующий интерфейс IXMLDocument. Некоторые из них:
a). Использовать компонент TXMLDocument:
TXMLDocument *XMLDocument = new TXMLDocument( this );Либо просто поместить одноименный компонент с вкладки Internet на одну из форм приложения.b). Xmldoc::NewXMLDocument
Xmlintf::_di_IXMLDocument xmlDocument = Xmldoc::NewXMLDocument();c). Xmldoc::LoadXMLDocument
Xmlintf::_di_IXMLDocument xmlDocument = Xmldoc:: LoadXMLDocument («D:\\db.xml»);Как видим, в данном варианте одновременно с созданием объекта происходит парсинг указанного файла.Для настройки поведения созданного объекта используются следующие свойства:
- NodeIndentStr – определяет строку-разделитель между вложенными узлами
- ParseOptions - определяет различные аспекты разбора (парсинга входного xml-файла
- Encoding – определяет кодировку XML-файла
- Version - определяет версию XML
- Options - определяет различные аспекты поведения документа
- DOMVendor - задает реализацию DOM, используемую для разбора входного xml-файла (имеются MSXML, Open XML, Xerces XML). Данное свойство имеется только у компонента TXMLDocument, и как задавать вендора (если и можно) для интерфейсных объектов я узнавать не стал.
Такс, что дальше…? Ах да, пишем же на С++, а значит не забываем про память, точнее ее очистку. И тут приятная новость – все уже сделано за нас.
Смотрите, если мы используем компонент TXMLDocument и при его создании передавали в конструктор указатель на родительскую форму (свойство Owner), то уничтожение компонента произойдет в момент уничтожения формы-родителя (это, вообщем-то, дефакто для VCL). Тут не важно как создавался компонент - с помощью drag-n-dropа на форму или ручками в коде, главное корректно установить свойство Owner (при drag-n-dropе это произойдет автоматически).
Если же мы используем интерфейсные объекты, полученные из методов NewXMLDocument, LoadXMLDocument, то освобождение ресурсов объекта произойдет автоматически, после закрытия (отвязки) всех ссылок на объект.
2. Создание xml-файла заданной структуры.
Следующий код создает xml-файл, рассмотренный выше.
- // получаем интерфейсный объект
- Xmlintf::_di_IXMLDocument m_xmlDocument = Xmldoc::NewXMLDocument();
-
- // указываем внутренную кодировку xml-файла
- m_xmlDocument->Encoding = "UTF-8";
-
- // создаем корневой узел (root node)
- // По стандарту XML - в документе может быть лишь один root node
- m_xmlDocument->AddChild( "users" );
-
- // внутренняя структура, представляющая пользовательские данные
- struct TUserInfo
- {
- int Uid; // id пользователя
- String Name; // имя пользователя
- String Comment; // другая информация о пользователе
- //..конструктор
- TUserInfo ( int _uid, String _name, String _comment ):
- Uid(_uid), Name(_name), Comment(_comment) {}
- };
-
- // данные о пользователях. Именно эти данные мы и должны записать в XML-документ
- int userUid = 1;
- TUserInfo arrUsers[] =
- {
- TUserInfo( userUid++, "Homer Simpson", "Отец семейства" ),
- TUserInfo( userUid++, "Marge Simpson", "Красавица жена" ),
- TUserInfo( userUid++, "Bart Simpson", "Малолетний озорник" ),
- TUserInfo( userUid++, "Lisa Simpson", "Маленький гений" )
- };
-
- // кол-во пользователей вычисляем динамически
- unsigned numUsers = sizeof(arrUsers) / sizeof(TUserInfo);
-
- // cоздаем узлы(секции) для каждого из пользователей
- for( unsigned i = 0; i < numUsers; i++ )
- {
- //..добавляем секцию-узел user
- IXMLNode *addingNode = m_xmlDocument->DocumentElement->AddChild( "user" );
-
- //..устанавливаем атрибут id для узла user
- addingNode->SetAttribute( "uid" , arrUsers[i].Uid );
-
- //..в секци user добавляем узел name
- addingNode->AddChild( "name" )->Text = arrUsers[i].Name;
-
- //..в секцию user добавляем узел comment
- addingNode->AddChild( "comment" )->Text = arrUsers[i].Comment;
- }
-
-
- // сбрасываем произведенные изменения в XML-файл
- // Если файл существует, то будет перезаписен, иначе создан.
- m_xmlDocument->SaveToFile( "D:\\db.xml" );
* This source code was highlighted with Source Code Highlighter.3. Считывание информации из существующего XML-файла с известной структурой
- // загружаем описание XML-файл
- m_xmlDocument->LoadFromFile( "D:\\db.xml" );
-
- // проверяем корректность корневого узла - он должен существоват и иметь имя "users"(case-sensitive)
- // DocumentElement - описание корневого узла
- if( m_xmlDocument->GetChildNodes()->Count < 1 ||
- m_xmlDocument->DocumentElement->LocalName != String("users")
- )
- {
- throw ( Exception("Отсутсвует корневая секция") );
- }
-
- // кол-во пользователей, описанных в XML-файле
- unsigned numUsers = m_xmlDocument->DocumentElement->GetChildNodes()->Count;
-
- // считываем информацию о кажом пользователе
- for( unsigned i = 0; i < numUsers; i++ )
- {
- //..получаем описание узла "user"
- IXMLNode *userNode = m_xmlDocument->DocumentElement->GetChildNodes()->Get(i);
-
- //..получаем uid пользователя. Если атрибута с таким именем нет, произойдет исключение
- int uid = userNode->GetAttribute("uid");
-
- //..получаем другие данные пользователя. Если атрибутов с таким именем нет, произойдет исключение
- String name = userNode->GetChildNodes()->GetNode("name")->Text;
- String comment = userNode->GetChildNodes()->GetNode("comment")->Text;
- }
* This source code was highlighted with Source Code Highlighter.Если xml-файл, во время работы вашей программы меняется внешней средой, и вам надо учитывать эти изменения, то на помощь приходит свойство Active. При выставление данного свойства в true, происходит перезагрузка xml-файла (файл заново парсится) и загружается в DOM, после чего мы можем работать с данными через интерфейсный объект IXMLDocument. При присваивании же свойству Active значений false, все хранимые в объекте данные (DOM) уничтожаются. Может чего понял и неправильно, буду рад если кто поправит.Не смущайтесь, что мы не задействовали свойство Active в предыдущем листинге. Там оно автоматически выставится в true в момент вызова метода LoadFromFile.m_xmlDocument->Active = true; // перезагружаем xml-файл в DOM
// … работаем с данными, находящимися в m_xmlDocument (DOM)
m_xmlDocument->Active = false; // данные в m_xmlDocument (DOM) теряются
4. Редактирование существующих записей, добавление новых.
Не думаю, что есть смысл приводить код для данных операций. По сути, создавая XML-файл, мы занимались добавлением новых пользователей. А редактирование существующих записей (узлов) – это тоже считывание xml-файла известной структуры, но с изменением левого и правого операндов:
userNode->GetChildNodes()->GetNode("name")->Text = name Отмечу лишь, что все производимые изменения, окажутся на диске только после вызова метода SaveToFile.m_xmlDocument->SaveToFile( "D:\\db.xml" ); 5. Исключения
Будьте готовы, что практически каждый метод интерфейса IXMLDocument может сгенерировать исключение: отсутствие файла на диске, отсутствие запрашиваемого узла и др. Более того, как показала практика, часть исключений совершенна неинформативна и скорее всего придется писать свои обертки для пользовательской логики.
Комментариев нет:
Отправить комментарий