XSLT (eXtensible Stylesheet Language Transformations) - язык преобразований xml-документов.
Задача генерирования отчетности в системе DIRECTUM является одной из наиболее востребованных.
В этой статье описан возможно не самый широко распространенный способ формирования html-отчетов на основе xslt-преобразований. Но несомненно заслуживающий внимания, как один из наиболее удобных и наглядных (если сравнить например с rtf-отчетом).
Если очень коротко, то xslt-преобразование заключается в трансформации xml-схемы с данными в отчет на основе предварительно подготовленного шаблона.
Для примера отчета описанного в статье будет выводится список контрагентов с их наименованием, ИНН и адресом.
Чтобы сформировать отчет понадобятся две составляющие:
- xml-данные для отчета
- xsl-шаблон отчета
Данные в формате xml можно получить прямым sql-запросом:
select Analit, -- ИД записи для генерации ссылки
NameAn, -- Наименование контрагента
EdIzm, -- ИНН
Dop2 -- Адрес
from dbo.MBAnalit
where Vid = %s
and Sost = 'Д'
and XRecStat = '+'
for xml path('org'), type -- Получить набор строк <org/> в виде xml
Описанный выше запрос вернет данные в таком формате:
<org><Analit>101702</Analit><NameAn>Мобил-Авто ООО</NameAn><EdIzm>123456789</EdIzm><Dop2>426000, г. Ижевск, ул. Революционная, 44</Dop2></org>
<org><Analit>148965</Analit><NameAn>ОАО Тринити</NameAn><EdIzm>631000001</EdIzm><Dop2>443000, Самарская обл., г. Самара, ул. Ленина, 11</Dop2></org>
<org><Analit>148966</Analit><NameAn>ООО Дальний восток</NameAn><EdIzm>011101001</EdIzm><Dop2>100006, г. Владивосток, ул. Первая, 1</Dop2></org>
Данные нужно обернуть в тег <orgs></orgs> и сохранить в файл, добавив заголовок:
<?xml version="1.0" encoding="windows-1251"?>
<?xml-stylesheet type="text/xsl" href="file:///С:/Temp/template.xsl" ?>
Вторая строка заголовка xml-файла данных важна, в ней указано, что в файле C:\Temp\template.xsl хранится шаблон отчета в который необходимо передать данные.
В итоге получится такой xml-файл:
<?xml version="1.0" encoding="windows-1251"?>
<?xml-stylesheet type="text/xsl" href="file:///С:/Temp/template.xsl" ?>
<orgs>
<org><Analit>101702</Analit><NameAn>Мобил-Авто ООО</NameAn><EdIzm>123456789</EdIzm><Dop2>426000, г. Ижевск, ул. Революционная, 44</Dop2></org>
<org><Analit>148965</Analit><NameAn>ОАО Тринити</NameAn><EdIzm>631000001</EdIzm><Dop2>443000, Самарская обл., г. Самара, ул. Ленина, 11</Dop2></org>
<org><Analit>148966</Analit><NameAn>ООО Дальний восток</NameAn><EdIzm>011101001</EdIzm><Dop2>100006, г. Владивосток, ул. Первая, 1</Dop2></org>
</orgs>
Если открыть полученный xml-файл, браузер будет использовать указанный в заголовке шаблон и подставит данные в него.
Далее необходимо создать шаблон, имя файла которого указано в заголовке xml-файла. xsl-шаблон отчета представляет собой обыкновенную html-страницу с особым заголовком:
<?xml version="1.0" encoding="windows-1251"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<style>
.main{
font-family:"Segoe UI", Arial;
font-size:12pt;
background-color:#fafafa
}
</style>
<body class="main">
<h3>Список контрагентов</h3>
<!-- Сюда вставить данные -->
</body>
</html>
В тэге <html> указаны версия и ссылка пространства имен xsl-преобразования. В остальном шаблон - это обыкновенная html-страница.
Теперь нужно сотворить немного магии и указать какие именно данные из xml и в каком виде вставить в шаблон отчета.
Для работы с данными в xsl-шаблоне используются специальные теги.
Чтобы перебрать все узлы из xml-файла данных нужно использовать конструкцию:
<xsl:for-each select="orgs/org">
</xsl:for-each>
Она позволит перебрать все дерево данных. Для того, чтобы выбрать только определенные данные из дерева используется правило выбора select="orgs/org". Правила описаны на языке запросов XPath. В текущем примере будет последовательно отобрана каждый узел <org> из <orgs>.
Если необходимо, чтобы записи отображались не в порядке следования в xml-файле, а сортировались, то нужно указать это в шаблоне:
<xsl:sort select="NameAn" order="ascending"/>
Теперь записи будут перебираться предварительно отсортированными по наименованию.
Но просто перебрать не достаточно, нужно вывести в отчет нужные данные, для этого существует конструкция:
<xsl:value-of select="NameAn"/>
Конструкция отобразит значение тэга <NameAn> (наименование контрагента).
Чтобы сделать из наименования контрагента ссылку, по которой можно будет открыть запись справочника, нужно немного модифицировать код:
<a target="_blank">
<xsl:attribute name="href">http://directum/reference.asp?sys=DIRECTUM&compcode=ОРГ&id=<xsl:value-of select="Analit"/></xsl:attribute>
<xsl:value-of select="NameAn"/>
</a>
Пусть ИНН контрагента отображается в отчете не просто так. Если ИНН в карточке отсутствует, то будет выводится сообщение об отсутствии ИНН. Для этого необходимо использовать конструкцию:
<xsl:choose>
<xsl:when test="EdIzm != ''">
<span>ИНН:<xsl:value-of select="EdIzm"/>. </span>
</xsl:when>
<xsl:otherwise>
<span class="red">ИНН не указан. </span>
</xsl:otherwise>
</xsl:choose>
В конструкции choose с помощью фильтра test проводится проверка содержимого тэга <EdIzm> (ИНН) на пустое значение (<xsl:when test="EdIzm !='' ">). Если содержимое не пусто, то выводиться значение. В других случаях выводится сообщение "ИНН не указан".
С помощью фильтра возможно реализовать еще одну "рюшечку", которая будет помечать контрагентов с ИНН начинающихся например с кода региона "18". Выглядеть это будет так:
<xsl:if test="starts-with(EdIzm,'18')">
<span class="red">♥ </span>
</xsl:if>
Конструкция для всех записей ИНН которых начинается с "18" дополнительно выведет значок ♥.
В конечном итоге получится такой код шаблона:
<?xml version="1.0" encoding="windows-1251"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<style>
.main{
font-family:"Segoe UI", Arial;
font-size:12pt;
background-color:#fafafa
}
.nameRow{
background-color:white;
color:gray;
padding:4px
}
.infoRow{
margin-left:20px;
margin-bottom:1em;
font-size:10pt
}
.fontBold{
font-weight:bold
}
.fontItalic{
font-style:italic
}
.red{
color:red;
}
</style>
<body class="main">
<h3>Список контрагентов</h3>
<xsl:for-each select="orgs/org">
<xsl:sort select="NameAn" order="ascending"/>
<div class="nameRow">
<xsl:if test="starts-with(EdIzm,'18')">
<span class="red">♥ </span>
</xsl:if>
<span class="fontBold">
<a target="_blank">
<xsl:attribute name="href">http://directum/reference.asp?sys=DIRECTUM&compcode=ОРГ&id=<xsl:value-of select="Analit"/></xsl:attribute>
<xsl:value-of select="NameAn"/>
</a>
</span>
</div>
<div class="infoRow">
<p>
<xsl:choose>
<xsl:when test="EdIzm != ''">
<span>ИНН:<xsl:value-of select="EdIzm"/>. </span>
</xsl:when>
<xsl:otherwise>
<span class="red">ИНН не указан. </span>
</xsl:otherwise>
</xsl:choose>
<span>Адрес: <xsl:value-of select="Dop2"/>.</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
А если открыть xml-файл в браузере, то получится такой отчет:
В статье описаны только несколько базовых возможностей xsl, такие как фильтры, сортировка, цикл, case-конструкция. Кроме них существуют еще интересные возможности. Надеюсь, статья подтолкнет изучить и их. Возможно после этого построение отчетов станет более интересным.
А если к отчету подключить такой мощный инструмент как javascript, то описанный отчет легко можно сделать интерактивным. Например добавить элемент, позволяющий проводить какую-либо фильтрацию. Или отображать графики по данным отчета.
Какие есть плюсы при реализации отчетов описанным способом:
- Требуется разработка относительно небольшого количества прикладных вычислений;
- Разработка шаблона наглядна и также относительно нетрудоёмка.
Всё описанное в статье легко интегрируется в аналитический отчет, ссылка на который приведена ниже.
Пример xsl отчета
Собственно отображение задач в Директум выполняется как раз таким образом.
Опять же трансформацию можно производить программно:
Не увидел в статье ответа на вопрос зачем нам выгружать данные сначала в xml-файл, а потом еще и писать xslt (который еще и изучить надо), если можно сразу выгрузить в формате html (по сути это тот же xml).
Аргументируйте, пожалуйста, и приведите примеры, применимые к отчетам Directum.
Обычно разделение данных и представления делают когда надо одни и те же данные надо по-разному отображать (отображать в нескольких представлениях динамически формируемые данные или если данные хранятся где-то и отображение может со временем меняться).
С отображением схем задач понятно почему надо использовать xslt - у нас данные уже есть, их надо только отобразить. Я не встречал никогда чтобы данные еще где-то хранились в xml и их надо было только отобразить.
К недостаткам представленного подхода еще можно отнести то, что xsl-файл еще надо где-то размещать, чтобы отчет можно было переслать по почте, например.
Александр, статья писалась для разработчиков, которые еще не слышали (или слышали, но краем уха) про xslt. И призвана подтолкнуть их к изучению этой темы.
По поводу того "зачем нам выгружать данные сначала в xml-файл" - можно и не выгружать, а реализовать по-другому, как например выше в комментарии описал Роман.
"а потом еще и писать xslt (который еще и изучить надо)" - именно для этого статья и писалась, чтобы начать изучать xslt.
Про "сразу выгрузить в формате html" - чтобы это сделать, все равно нужно вычисления писать, которые данные "обернут" в тэги html-разметки. И такой путь не соответсвует парадигме MVC. Если отчетик небольшой и известно, что никаких изменений в нем не предвидится, то, возможно, отступить от этой парадигмы не страшно.
Но если предполагается, что периодически необходимо вносить доработки в отчет, то поддержка такого отчета станет трудоемкой задачей.
К примеру сейчас сущетсвует достаточно много государственных сервисов, которые выдают информацию в виде xml. Эти данные можно сразу отображать в удобно перевариваемом виде, в нужной форме.
А если случится так, что форма видоизменится и/или в xml добавятся новые (или удалятся старые) реквизиты, то достаточно подкорректировать xsl-шаблон, чтобы форма отчета осталась актуальной не проводя при этом доработок вычислений.
Что касается размещения xsl-файла. То размещать его можно, к примеру, в аналитическом отчете. А чтобы отправить отчет сформированный по xsl-шаблону сам шаблон уже не нужен, можно отправить html-страницу построенную на основе шаблона.
Алексей,
Если у нас уже есть xml и нам надо его отобразить пользователю, то я согласен что надо использовать xslt.
Не надо показывать незнающим разработчикам что надо сначала выгружать sql-запросом в xml, учить xsl и писать преобразование. Думаю, нужна статья, показывающая, что в большинстве случаев html можно и надо формировать прямо на sql. Отображение sql -> html частая задача, xml->html редкая. Изучать что-то новое (при этом сомнительно удобное) и делать на нем все подряд отчеты не стоит. Потому что тем, кому достанется такое наследие, тоже придется учить xslt...
Разделить данные и представление можно в рамках одного sql-запроса. Так что выгружать сначала в xml, а потом его преобразовывать в HTML xslt нет особого смысла, ведь промежуточный xml нигде использоваться не будет. Вот как выглядит sql-запрос для такого же результата:
Это я всё к тому, что статья в каком-то неправильном ключе подана. Сам по себе материал полезен, но логичнее было бы сразу указать, что такой подход стоит использовать если уже есть xml или он для чего-то нужен, а не как промежуточное звено. А лучше сначала написать статью про формирование html-отчетов с помощью for xml, про эту штуку думаю тоже мало народу знает, хотя она ну очень удобная. SQL знают все, учить почти ничего нового не надо.
Александр,
Выше я уже написал, что статья для того, чтобы подтолкнуть разработчиков к изучению xsl. И приведенный пример не является идеальным ни с технической, ни с точки зрения бизнес-процессов. Это просто демо-пример с помощью которого описаны базовые xsl-конструкции.
Отвечу на некоторые Ваши утверждения, которые кажутся мне спорными:
"Отображение sql -> html частая задача, xml->html редкая" - очень спорное утверждение. Слишком много ньюансов для решения различных задач и в каждой задаче луший вариант необходимо выбирать. Универсального нет.
"лучше сначала написать статью про формирование html-отчетов с помощью for xml" - думаю, тема тоже интересная, но эта статья именно про xsl.
"SQL знают все, учить почти ничего нового не надо" - тоже спорное утверждение.
"Разделить данные и представление можно в рамках одного sql-запроса" - можно. Но далеко не всегда нужно. Все-таки основная задача SQL-сервера не в этом. Особенно это касается крупных заказчиков, у которых SQL-сервер загружен "основной" работой. И, опять же, цель статьи была - рассказать именно про xsl.
И по результатам переписки, думаю Вам есть что рассказать по тематике отображения данных базы данных в виде html-страницы. Если бы такая статья(и) появились на клабе, то было бы очень здорово. Сам бы с удовольствием почитал.
Алексей, давайте не уходить в сторону от основного вопроса, он звучит так: зачем мне делать SQL->XML->[xslt]->HTML, если можно SQL->HTML.
1) Если у нас уже есть XML (в таком формате хранятся данные или в таком формате откуда-то приходит), то я согласен, что хорошим вариантом отображения является использование XSLT. Собственно статья об этом (а не о том, что ВООБЩЕ отчеты хорошо и удобно строить используя xslt). И это надо указать в статье, и не приводить смущающих примеров (с выгрузкой в XML с SQL) и сравнивать с rtf. Подавляющее большинство отчетов в Directum строится на основе данных из SQL-таблиц, а не XML.
2) Аргумент про то, что это не задача SQL - он смешной. SQL это делать умеет, и делает это быстро, дешево и удобно. По сравнению с "лишней" нагрузкой, которую создает работа с ОМ и говнокод, работа с XML - просто семечки. А еще напомню про существование приложений (я лично работал с WebTutor), для которых XML - чуть ли не основной тип хранимых данных и все выборки и преобразования XML производятся в БД. Ну и писать такое и в примере делать SQL->XML как-то нелогично.
3) Чтобы использовать Ваш подход, надо знать и SQL->XML и XSL. Мой же вариант проще, достаточно знать SQL->XML(HTML). Я не услышал ни одного аргумента в пользу SQL->XML->[xslt]->HTML. Сначала Вы говорите, что мой подход "не соответсвует парадигме MVC", а когда я пишу что данные и представление можно разделить и в рамках одного SQL-запроса и XML в вашем подходе ИСПОЛЬЗУЕТСЯ ТОЛЬКО КАК ПРОМЕЖУТОЧНОЕ ЗВЕНО (а не как модель), Вы пишете что разделять модель и представление "далеко не всегда нужно".
Вы можете придумать конкретный пример, когда нам гораздо удобнее и разрабатывать и поддерживать веб-отчет используя SQL->XML->[xslt]->HTML, а не SQL->HTML? Я лично изучал xsl, пытался пользоваться. Но за 5 лет реально что-то делал SQL->XML->[xslt]->HTML 1 раз и то потом переделал на SQL->HTML потому что xsl было излишеством, которое, к тому же, мало кто знал. Я видел веб-контролы в канцелярии на xslt и мое мнение - они слишком сложны для такой простой задачи. SQL->HTML я использовал раз 50 наверно уже (как для простых задач, так и для сложных 1, 2 etc). И не потому что не знаю xsl, а потому что SQL->HTML проще и удобнее. Возможно мне не попадались подходящие задачи, но такой вот у меня опыт. А Вы сколько раз делали SQL->XML->[xslt]->HTML и SQL->HTML?
Авторизуйтесь, чтобы написать комментарий