Объектная модель XML-парсера — различия между версиями
Строка 111: | Строка 111: | ||
isubset.Add ( CMapperXmlItem.GetItem ( "id", "0" ) ); | isubset.Add ( CMapperXmlItem.GetItem ( "id", "0" ) ); | ||
isubset.Add ( CMapperXmlItem.GetItem ( "name", "Товары" ) ); | isubset.Add ( CMapperXmlItem.GetItem ( "name", "Товары" ) ); | ||
− | + | ||
<span style="color:green">//Второй подэлемент</span> | <span style="color:green">//Второй подэлемент</span> | ||
isubset = new CMapperXmlItemSet ( ); | isubset = new CMapperXmlItemSet ( ); |
Версия 15:22, 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-программы</span> 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 ) { //Исключение при обработке. } } }