Главная » XSLT » Генерация кода XML – Введение

0

Автоматизация – это Святой Грааль в разработке программного обеспече­ния. Вообще, прогресс в этой области в значительной мере обязан идее генера­ции кода по той или иной спецификации. Разве не этим занимаются ассемблеры и компиляторы? Но есть и другой вид генерации кода, когда целью является не исполняемый машинный код, а программа на языке высокого уровня, напри­мер, Java или C++. Зачем это может понадобиться и какое к этому отношение имеет XML?

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

Эта глава отличается от остальных тем, что большинство примеров в ней – компоненты решения в контексте конкретного приложения. Для такого построе­ния есть две причины.

Во-первых, маловероятно, что вы будете кодировать информацию в формате XML для последующей генерации кода только потому, что XML – это «круто». Как правило, предстоит решить некую крупную задачу, в которой XML можно использовать и для других целей. Поэтому и примеры в этом разделе станут более осмысленными, если представить их в контексте объемлющей задачи.

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

Итак, в чем же состоит объемлющая задача?

Представим себе сложное клиент-серверное приложение. Говоря сложное, я имею в виду, что оно состоит из многочисленных процессов на стороне сервера и клиента. Эти процессы обмениваются между собой данными в виде сообщений, передаваемых с помощью связующего программного обеспечения для передачи сообщений (от точки к точке, типа публикация/подписка или то и другое). В каче­стве примеров таких программ можно назвать IBM MQSeries, Microsoft Message Queuing (MSMQ), BEA Systems Tuxedo и TIBCO Rendezvous. Для нас неважно, какой конкретно продукт используется. А важно то, что основную работу система выполняет при получении сообщения, в ответ на которое должна послать в ответ одно или несколько сообщений1. Сообщение может содержать XML (в виде SOAP), неразмеченный текст или двоичные данные. В главе 12 мы рассмотрим протокол SOAP в контексте WSDL. А сейчас нас интересуют межсерверные ком­муникации, в которых XML применяется реже.

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

Но тут же возникает следующий вопрос: как разобраться в языке межпроцесс­ного общения в сложном приложении? Есть ли какое-то одно место, где можно почерпнуть всю информацию? Увы, зачастую это не так. Очень редко удается найти актуальную и полную спецификацию прикладных протоколов. Отыскать кусочки головоломки можно в различных заголовочных файлах и в проектной документации, разрабатывавшейся на протяжении жизненного цикла системы, но единого места, в котором была бы собрана вся жизненно важная информация, не существует. Во многих случаях единственный надежный способ получить ее – изучить исходный код, от чего я как раз и предостерегал в самом начале!

Ну хорошо, а какое отношение все это имеет к XML, XSLT и, главное, к гене­рации кода? Собственно, проблема состоит в отсутствии документации, детально описывающей структуру межпроцессных сообщений в приложении. Как мог бы выглядеть такой документ? Разработчики могли бы поддерживать в актуальном состоянии созданный в MS Word документ, в котором описаны все сообщения. Еще лучше, если бы этой теме был посвящен специальный сайт, поддерживающий нави­гацию и поиск. А, быть может (и вы, конечно, сами догадались!), информацию стоит хранить в XML-файле! Тогда из этого файла можно было бы сгенерировать и сайт. И уж раз такой файл имеется, то почему бы не попытаться сгенерировать хотя бы частично код, необходимый для обработки описанных сообщений. Именно этим мы и займемся в настоящей главе. Я буду называть множество XML-файлов репозиторием межпроцессных сообщений. В рецептах ниже демонстрируется, как генерировать код на основе такого репозитория.

Но, прежде чем переходить к рецептам, поговорим о структуре репозито- рия. Формально она описывается в виде схемы W3C XSD Schema, но мы пред­ставим только интуитивно понятный рисунок для тех, кто со схемами XML не знаком.

Рис. 12.1 был создан программой Altova’s XML Spy 4.0 (http://www.xmlspy.com). Значок троеточия обозначает упорядоченную последовательность. Значок, напо­минающий трехпозиционный переключатель, обозначает выбор.

Хотя этой схемы и достаточно для иллюстрации интересных рецептов генера­ции кода, до промышленного репозитория сообщений она не дотягивает. В репо- зитории можно было бы хранить следующие дополнительные данные:

?               символические константы, описывающие размеры массивов и строк, а так­же значения, употребляемые в перечислениях;

?               информацию о сложных типах данных, например, объединениях и псевдо­нимах типов (typedef в языке C);

?               информацию о протоколах (последовательностях сообщений, которыми процессы обмениваются для достижения конкретного результата);

?               информацию об авторах оригинальной версии, датах и авторах измене­ний и т.п.;

?               информацию о доставке и транспортировке: имена издателей и подписчи­ков либо имена очередей.

В качестве примера рассмотрим простое клиент-серверное приложение, кото­рое позволяет отправлять заказы на склад или отменять их. Репозиторий для по­добного приложения мог бы выглядеть так:

<MessageRepository xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:

noNamespaceSchemaLocation="C:\MyProjects\XSLT Cookbook\code

gen\MessageRepository.xsd"> <DataTypes> <Primitive>

<Name>Real</Name> <Size>8</Size> <Category>real</Category> </Primitive> <Primitive>

<Name>Integer</Name>

Рис. 12.1. Графическое представление XSD-схемы репозитория

<Size>4</Size>

<Category>signed integer</Category> с/Primitive> Primitive> <Name>StkSymbol</Name> <Size>10</Size> <Category>string</Category> с/Primitive> Primitive> <Name>Message</Name> <Size>100</Size> <Category>string</Category> с/Primitive>

<Primitive>

<Name>Shares</Name> <Size>4</Size>

<Category>signed integer</Category> </Primitive> <Enumeration>

<Name>BuyOrSell</Name> <Enumerators> <Enumerator>

<Name>BUY</Name> <Value>0</Value> </Enumerator> <Enumerator>

<Name>SELL</Name> <Value>1</Value> </Enumerator> </Enumerators> </Enumeration> <Enumeration>

<Name>OrderType</Name> <Enumerators> <Enumerator>

<Name>MARKET</Name> <Value>0</Value> </Enumerator> <Enumerator>

<Name>LIMIT</Name> <Value>1</Value> </Enumerator> </Enumerators> </Enumeration> <Structure>

<Name>TestData</Name> <Members> <Member>

<Name>order</Name>

<DataTypeName>AddStockOrderData</DataTypeName> </Member> <Member>

<Name>cancel</Name>

<DataTypeName>CancelStockOrderData</DataTypeName> </Member> </Members> </Structure>

<Name>AddStockOrderData</Name>

<Documentation>Запрос на добавление нового заказа.</Documentation> <Members> <Member>

<Name>symbol</Name>

<DataTypeName>StkSymbol</DataTypeName> </Member> <Member>

<Name>quantity</Name> <DataTypeName>Shares</DataTypeName> </Member> <Member>

<Name>side</Name>

<DataTypeName>BuyOrSell</DataTypeName> </Member> <Member>

<Name>type</Name>

<DataTypeName>OrderType</DataTypeName> </Member> <Member>

<Name>price</Name>

<DataTypeName>Real</DataTypeName> </Member> </Members> </Structure> <Structure>

<Name>AddStockOrderAckData</Name>

<Documentation>Подтверждение успешного добавления заказа. </Documentation> <Members> <Member>

<Name>orderId</Name>

<DataTypeName>Integer</DataTypeName> </Member> </Members> </Structure> <Structure>

<Name>AddStockOrderNackData</Name>

<Documentation>Cообщение об ошибке при добавлении заказа. </Documentation> <Members> <Member>

<Name>reason</Name>

<DataTypeName>Message</DataTypeName> </Member> </Members> </Structure> <Structure>

<Name>CancelStockOrderData</Name>

<Documentation>3anpoc на полную или частичную отмену заказа</ Documentation> <Members> <Member>

<Name>orderId</Name>

<DataTypeName>Integer</DataTypeName> </Member> <Member>

<Name>quantity</Name> <DataTypeName>Shares</DataTypeName> </Member> </Members> </Structure> <Structure>

<Name>CancelStockOrderAckData</Name>

<Documentation>Пoдтвepждeниe успешной отмены заказа. </Documentation> <Members> <Member>

<Name>orderId</Name>

<DataTypeName>Integer</DataTypeName> </Member> <Member>

<Name>quantityRemaining</Name> <DataTypeName>Shares</DataTypeName> </Member> </Members> </Structure> <Structure>

<Name>CancelStockOrderNackData</Name>

<Documentation>Cooбщeниe об ошибке при отмена зaкaзa.</Documentation> <Members> <Member>

<Name>orderId</Name>

<DataTypeName>Integer</DataTypeName> </Member> <Member>

<Name>reason</Name>

<DataTypeName>Message</DataTypeName> </Member> </Members> </Structure> </DataTypes> <Messages> <Message>

<Name>ADD_STOCK_ORDER</Name> <MsgId>1</MsgId>

<DataTypeName>AddStockOrderData</DataTypeName> <Senders>

<ProcessRef>StockClient</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockServer</ProcessRef> </Receivers> </Message> <Message>

<Name>ADD_STOCK_ORDER_ACK</Name> <MsgId>2</MsgId>

<DataTypeName>AddStockOrderAckData</DataTypeName> <Senders>

<ProcessRef>StockServer</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockClient</ProcessRef> </Receivers> </Message> <Message>

<Name>ADD_STOCK_ORDER_NACK</Name> <MsgId>3</MsgId>

<DataTypeName>AddStockOrderNackData</DataTypeName> <Senders>

<ProcessRef>StockServer</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockClient</ProcessRef> </Receivers> </Message> <Message>

<Name>CANCEL_STOCK_ORDER</Name> <MsgId>4</MsgId>

<DataTypeName>CancelStockOrderData</DataTypeName> <Senders>

<ProcessRef>StockClient</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockServer</ProcessRef> </Receivers> </Message> <Message>

<Name>CANCEL_STOCK_ORDER_ACK</Name> <MsgId>5</MsgId>

<DataTypeName>CancelStockOrderAckData</DataTypeName> <Senders>

<ProcessRef>StockServer</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockClient</ProcessRef> </Receivers> </Message> <Message>

<Name>CANCEL_STOCK_ORDER_NACK</Name> <MsgId>6</MsgId>

<DataTypeName>CancelStockOrderNackData</DataTypeName> <Senders>

<ProcessRef>StockServer</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockClient</ProcessRef> </Receivers> </Message> <Message>

<Name>TEST</Name> <MsgId>7</MsgId>

<DataTypeName>TestData</DataTypeName> <Senders>

<ProcessRef>StockServer</ProcessRef> </Senders> <Receivers>

<ProcessRef>StockClient</ProcessRef> </Receivers> </Message> </Messages> <Processes> <Process>

<Name>StockClient</Name> </Process>

<Process>

<Name>StockServer</Name> </Process> </Processes>

</MessageRepository>

Этот репозиторий описывает сообщения, которыми обмениваются клиент (он называется StockClient) и сервер (StockServer) для выполнения различных операций. Читатели, знакомые с языком WSDL, отметят несомненное сходство; однако WSDL применяется для описания Web-сервисов и используется в контек­сте протокола SOAP, хотя технически его спецификация ни от какого протокола не зависит (http://www.w3.org/TR/wsdl).

Последние два примера в этой главе не связаны с задачей обмена сообщени­ями. Первый из них посвящен генерации кода на языке C++ из модели на уни­фицированном языке моделирования (UML), которая экспортируется инстру­ментом разработки в виде файла на языке XMI (XML Metadata Interchange – обмен XML-метаданными). Во втором обсуждается применение XSLT для гене­рации XSLT.

Прежде чем переходить к самим примерам, хочу принести извинения за то, что в большинстве примеров используется язык C++. Я выбрал этот язык только потому, что хорошо с ним знаком; именно для него я написал реальные генерато­ры. Концептуально же весь каркас переносится и на другие языки, пусть даже фактический XSLT-код придется переделать1.

XSLT 2.0

В силу особенностей рецептов ниже я не стал приводить решения на XSLT 1.0 и 2.0 по отдельности, как в большинстве других глав. Читателям, которым инте­ресно, как применять для генерации кода XSLT 2.0, рекомендую обратиться в главе 6. Многие описанные там особенности XSLT 2.0 и способы их применения, подхо­дят и для генерации кода. Вот несколько общих рекомендаций:

?               Применяйте новый атрибут separator элемента xsl:value-of.

При генерации кода часто приходится порождать последовательности эле­ментов, разделенных какими-то символами (например, списки значений, разделенных запятыми). В таких случаях способность xsl:value-of ав­томатически вставлять разделитель может упростить генератор.

?               Пользуйтесь возможностями последовательностей в XPath 2.0.

Один из самых серьезных недостатков генераторов, написанных на XSLT 1.0, – это их громоздкость. В генераторах часто встречаются циклы и условные конструкции, а команды xsl:for-each и xsl:choose трудно назвать образцами краткости. Однако многие задачи генерации можно решить на XPath 2.0 вместо XSLT. Нередко XSLT и XPath применяются для преобразования входного XML в одну или несколько последовательностей, после чего с помощью XPath они отображаются на код:

<!— Функция для генерации объявления функции на языке C. –>

<!– Используется преимущественно XPath 2.0 –>

<xsl:function name="ckbk:function-decl" as="xs:string*"> <xsl:param name="name" as="xs:string"/> <xsl:param name="returnType" as="xs:string"/> <xsl:param name="argNames" as="xs:string*"/> <xsl:param name="argTypesPre" as="xs:string*"/> <xsl:param name="argTypesPost" as="xs:string*"/> <xsl:variable name="c" select="count($argNames)"/> <xsl:sequence select="$returnType,

$name, ‘(‘,

for $i in 1 to $c return ($argTypesPre[$i], $argNames[$i], $argTypesPost[$i], if ($i ne $c) then ‘,’ else ”), ‘);’ "/>

</xsl:function>

?               Модульный, повторно используемый XSLT-код проще писать в версии 2.0. Если вы собрались писать генераторы, позволяющие транслировать XML- описание на различные языки, то стоит получше освоить приемы, описан­ные в рецептах 6.3 и 6.4.

?               В версии 2.0 стандартизован вывод нескольких документов.

Авторы генераторов для языков типа C и C++, где заголовочные и исход­ные файлы разделены, наверняка оценят удобство стандартизованной ко­манды xsl:result-document для написания таблиц стилей, порождаю­щих несколько выходных документов.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии