Объектная модель XML-парсера — различия между версиями

Материал из Oktell
Перейти к: навигация, поиск
(Новая страница: «Наверх __TOC__ ==Объектная модель== Для удобства работы со строков...»)
(нет различий)

Версия 15:21, 31 марта 2014

Наверх

Объектная модель

Для удобства работы со строковыми XML параметрами разработчику изначально доступны исходные коды классов для преобразования строки установленного формата в структуру объектов, для которых доступны методы преобразования из строки и приведения к строке.

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


Объектная модель представлена тремя видами объектов-данных (по аналогии с используемым форматом структуры XML) и вспомогательного класса для преобразований.

  • Объект класса CMapperXmlItem представляет собой набор данных, описывающих одно свойство (соответствует элементам XML-структуры property_simple или property_cdata) и содержит поля Key, Value, Name, которые отражают значения соответствующих атрибутов у элементов с тэгами <property_...>. Значение поля CDATA элемента property_cdata также попадает в поле Name объекта. С точки зрения объектной модели при разборе строки XML не делается различий между property_simple и property_cdata. Однако у объекта есть дополнительное поле Type, которое определяет тип преобразования из объектной модели в строку с XML структурой. Тип CDATA рекомендуется к использованию для свойств, имеющих большой или неизвестный заранее объем или формат текстовых данных. При работе с объектами для формирования строки можно самостоятельно устанавливать подходящий для конкретного случая тип свойства. А можно пользоваться уже встроенным статическим методом класса GetItem для автоматического определения типа по данным. При разборе значений можно пользоваться статическими методами класса GetValueName (возвращает значение поля Value, а если оно пусто, то Name) и GetNameValue (наоборот).
  • Объект класса CMapperXmlItemSet представляет собой набор свойств (соответствует элементу property_set), перечисленных в порядке следования безразлично к уникальности ключей. Объект класса имеет также поля Id и IdName, которые соответствуют атрибутам id и name элемента XML-структуры property_set. Дополнительно объект имеет поле Subsets - коллекция (объект класса CMapperXmlItemSetCollection), содержащая набор вложенных объектов CMapperXmlItemSet.
  • Объект класса CMapperXmlItemSetCollection (соответствует элементу property_collection) представляет собой набор объектов CMapperXmlItemSet, перечисленных в порядке следования безразлично к другим способам идентификации. Объект имеет поле IdName, соответствующее атрибуту name. Атрибут count в соответствующем элементе структуры XML отражает количество вложенных вложенных объектов CMapperXmlItemSet.
  • Класс СMapperXmlTransformer имеет только статические методы для преобразований из объектов в строку и наоборот.
  • При парсинге значений из строки в объектную модель в случае, если какой-либо атрибут в тэге не задан, в соответствующее поле объекта попадает пустое значение null.


Исходный код классов приведен в файле Xml.cs демонстрационной plugin-программы. Далее в этом разделе будет разъяснено на примере как производить работу с классами для создания строки с XML-структурой требуемого образца, а также обратной операции разбора параметров XML.


Формирование XML-строки требуемого образца

Рассмотрим на примере, как организовать код на C# для работы с объектной моделью. Решается задача формирования XML строки, имеющей следующий вид:

<?xml version="1.0" encoding="utf-16"?>
<oktellxmlmapper version="80710">

  <property_set>
    <property_simple key="paramkey" value="arbitraryarg" />
    <property_simple key="paramtype" value="4" name="argument" />
    <property_cdata key="paramname"><![CDATA[Имя оператора]]></property_cdata>
    <property_cdata key="paramdescription"><![CDATA[Подставляется в зачитываемый текст]]></property_cdata>
  </property_set>
  <property_set>
    <property_simple key="paramkey" value="categories" />
    <property_simple key="paramtype" value="7" name="fixedlist" />
    <property_cdata key="paramname"><![CDATA[Категории]]></property_cdata>
    <property_cdata key="paramdescription"><![CDATA[Выбор рабочего пространства]]></property_cdata>
    <property_collection name="categories" count="2">
      <property_set name="category" id="0">
        <property_simple key="id" value="0" />
        <property_cdata key="name"><![CDATA[Товары]]></property_cdata>
      </property_set>
      <property_set name="category" id="1">
        <property_simple key="id" value="1" />
        <property_cdata key="name"><![CDATA[Услуги]]></property_cdata>
      </property_set>
    </property_collection>
  </property_set>

</oktellxmlmapper> 

В XML структуре на первом уровне находятся 2 элемента property_set, не имеющие атрибутов, а имеющие только свойства (property_simple и property_cdata), второй элемент содержит коллекцию из двух субэлементов. У этих субэлементов присутствуют атрибуты имени и идентификатора.


Организация кода с использованием описанной объектной модели производится следующим образом:

//Создание главной коллекции, преобразование которой в XML будет производиться
CMapperXmlItemSetCollection coll = new CMapperXmlItemSetCollection ();

//Объявление переменных
CMapperXmlItemSet iset = null;
CMapperXmlItemSet isubset = null;
 
//Первый элемент коллекции (набор)
//  Подробный комментарий
 
//Создание объекта
iset = new CMapperXmlItemSet ( );
//Добавление в необходимую коллекцию (в данном случае в основную)
coll.Add ( iset );
 
//Формирование свойств элемента-набора
//  Происходит с помощью автоматического определения типа (simple/cdata)
 
//<property_simple key="paramkey" value="arbitraryarg"/>
iset.Add ( CMapperXmlItem.GetItem ( "paramkey", "arbitraryarg" ) );
//<property_simple key="paramtype" value="4" name="argument"/>
iset.Add ( CMapperXmlItem.GetItem ( "paramtype", 4, "argument" ) );
//<property_simple key="paramname"><![CDATA[Имя оператора]]></property_cdata>
iset.Add ( CMapperXmlItem.GetItem ( "paramname", "Имя оператора" ) );
//<property_simple key="paramdescription"><![CDATA[Подставляется в зачитываемый текст]]></property_cdata>
iset.Add ( CMapperXmlItem.GetItem ( "paramdescription", "Подставляется в зачитываемый текст" ) );
 
//Второй элемент коллекции (набор)
iset = new CMapperXmlItemSet ( );
coll.Add ( iset );
iset.Add ( CMapperXmlItem.GetItem ( "paramkey", "categories" ) );
iset.Add ( CMapperXmlItem.GetItem ( "paramtype", 7, "fixedlist" ) );
iset.Add ( CMapperXmlItem.GetItem ( "paramname", "Категории" ) );
iset.Add ( CMapperXmlItem.GetItem ( "paramdescription", "Выбор рабочего пространства" ) );

//У второго элемента есть вложенные
//  Происходит формирование коллекции вложенных элементов

//Задание свойства name вложенной коллекции
iset.SubSets.Name = "categories";

//Первый подэлемент
isubset = new CMapperXmlItemSet ( );
iset.SubSets.Add ( isubset );
isubset.Name = "category";
isubset.IdName = "0";                        
isubset.Add ( CMapperXmlItem.GetItem ( "id", "0" ) );
isubset.Add ( CMapperXmlItem.GetItem ( "name", "Товары" ) );  
//Второй подэлемент
isubset = new CMapperXmlItemSet ( );
iset.SubSets.Add ( isubset );
isubset.Name = "category";
isubset.IdName = "1";                        
isubset.Add ( CMapperXmlItem.GetItem ( "id", "1" ) );
isubset.Add ( CMapperXmlItem.GetItem ( "name", "Услуги" ) );

//Преобразование коллекции (объекта) к XML-строке
return coll.WriteToXml ( "input" );


Парсинг XML-строки

Рассмотрим на примере, как организовать код на C# для работы с объектной моделью. Решается задача выделения нужных параметров из простейшей XML-строки установленного образца, имеющей следующий вид:

<?xml version="1.0" encoding="utf-16"?>
<oktellxmlmapper version="80710">

  <property_set id="20101" name="tabchange">
    <property_simple key="idplugin" value="12341234-abcd-abcd-abcd-abcdabcdabcd" />
    <property_simple key="idform" value="f031ea44-7299-a173-4b1b-238fe1200c3" />
    <property_simple key="idshow" value="2e2d8ff0-de47-415f-ab68-9bddcd09952b" />
    <property_simple key="actiontype" value="20101" name="TabChange" />
    <property_simple key="activeindex" value="6" />
  </property_set>

</oktellxmlmapper>


Это строковый параметр, передаваемый в метод DoQuery для обработки события клиентского приложения о факте смены вкладки в заголовке модуля. Метод DoQuery принадлежит управляющему объекту, поэтому он должен выделить параметры: Код формы и экземпляра (для того чтобы перенаправить команду в нужном направлении, так как открытых форм может быть много), а также тип события и его параметр (в данном случае тип - смена вкладки, а параметр - код вкладки).


Очевидно, что приведенный пример - частный случай и не может в чистом виде быть использован. Необходима полная обработка возможных вариантов (конкретнее можно посмотреть в демонстрационно примере plugin-программы).

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

//Коллекция с запросом на основе XML-строки
CMapperXmlItemSetCollection coll = CMapperXmlItemSetCollection.FromXml ( xml );
if ( coll == null )
{
//Если что-то не корректно - соответствующая обработка
}
else
{
//Коллекция без ошибок была преобразована к объектной модели
 
//Цикл по всем наборам основной коллекции.
//  В зависимости от поставленной задачи: 
//  - либо ищем необходимый блок 
//       (тогда пропускаем все другие)
//  - либо обрабатывается/преобразуется вся структура 
//       (тогда в теле цикла по мере разбора производим обработку)
//  В некоторых случаях необходимо готовить ответ на запрос. 
//   Тогда объединяется процедура разбора и создания ответа (зависимо в едином теле цикла)
for ( int i1 = 0; i1 < coll.Count; i1++ )
{
   CMapperXmlItemSet iset = coll [ i1 ];
 
   //Необходимо обеспечить стабильность кода проверками корректности при преобразованиях,
   //  либо включить код в блок обработки исключений try..catch
   //  Например, если в поле, где заявлен для указания GUID, содержится некорректная строка
   try
   {
     //По имени набора определяется запрошенное действие
     //  (атрибут name тега property_set)
     switch ( iset.Name.ToLower() )
     {
       case "tabchange": //команда смены вкладки
 
         //Объявляем переменные, которые необходимы для обработки события
         Guid idplugin = Guid.Empty;
         Guid idform = Guid.Empty;
         Guid idshow = Guid.Empty;
         int index = -1;
 
         //Сначала в цикле обрабатываются все свойства поочередно
         //  безотносительно типа (property_simple/property_cdata)
         //  И только потом производится передача управления в метод обработки команды
         for ( int i2 = 0; i2 < iset.Count; i2++ )
         {
           //Объект - свойство
           CMapperXmlItem item = iset [ i2 ];
 
           //По значению атрибута key определяется что именно представляет из себя свойство
           //  В теле блока switch идет разбор только известных команд. Остальные пропускаются.
           switch ( item.Key.ToLower() )
           {
             case "idplugin"://свойство идентификатора plugin-программы
               idplugin = new Guid ( CMapperXmlItem.GetValueName ( item ) );
             break;
 
             case "idform"://свойство идентификатора plugin-формы</span>
               idform = new Guid ( CMapperXmlItem.GetValueName ( item ) );
             break;
 
             case "idshow"://свойство идентификатора экземпляра plugin-формы</span>
               idshow = new Guid ( CMapperXmlItem.GetValueName ( item ) );
             break;
 
             case "activeindex"://идентификатор выбранной вкладки</span>
               index = int.Parse ( CMapperXmlItem.GetValueName ( item ) );
             break;
           }
         }
 
         //Проверка корректности задания параметров
 
         //Элементарная проверка задания всех необходимых свойств
         if (( index < 0 ) || ( idform == Guid.Empty ) || ( idshow == Guid.Empty ))
         {
            //Обработка ошибки и выход
           break;
         }
 
         //При необходимости проверка корректности задания свойств 
         //  (наличие формы среди открытых, наличие указанной вкладки)
 
         //Обработка команды смены вкладки.
         //  Это внутренний вызов, может быть реализован любым способом.
         ActionChangeTab ( idform, idshow, index );
 
       break;
     }
   }
   catch ( Exception ex )
   {
     //Исключение при обработке.
   }
}
}