Преобразование
выходного потока XML-данных при выводе в броузер
средствами ASP.NET и XSLT.
Автор: Измайлов
Феликс (qstart@narod.ru)
Источник: http://rsl.bankir.ru/
Данная статья смещает акцент с работы ASP.NET
и MSSQL на использование языка шаблонов XSLT, но
в данной связке данные технологии представляют
собой мощное и гибкое средство представления
данных. Наложение шаблонов позволяет (и что
будет показано в данной статье) превратить поток
в таблицу HTML, преобразовать в Excel-файл, и
открыть его в браузере.
Небольшая выдержка из прошлой статьи, для
логичного продолжения материала. У нас есть код
который выводит в браузер XML-документ
(primer.aspx), немного изменим состав полей, для
краткости вывода.
<%@ Page Language="VB" %>
<%@ import Namespace="Microsoft.Data.SqlXML" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
Dim cmd as SqlXmlCommand
cmd = New SqlXmlCommand("Provider=SQLOLEDB;server=(local);database=pubs;uid=sa;password=sa")
cmd.CommandText ="SELECT Title,Price,Ytd_sales FROM Titles FOR XML AUTO"
Response.ContentType = "text/xml"
cmd.RootTag = "root"
Response.Clear()
cmd.ExecuteToStream(Response.OutputStream)
End SUb
</script>
В результате выполнения запроса получаем:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Titles Title="The Busy Executive's Database Guide" Price="19.99" Ytd_sales="4095" />
<Titles Title="Cooking with Computers: Surreptitious Balance Sheets" Price="11.95" Ytd_sales="3876" />
<Titles Title="You Can Combat Computer Stress!" Price="2.99" Ytd_sales="18722" />
<Titles Title="Straight Talk About Computers" Price="19.99" Ytd_sales="4095" />
<Titles Title="Silicon Valley Gastronomic Treats" Price="19.99" Ytd_sales="2032" />
<Titles Title="The Gourmet Microwave" Price="2.99" Ytd_sales="22246" />
<Titles Title="The Psychology of Computer Cooking" />
<Titles Title="But Is It User Friendly?" Price="22.95" Ytd_sales="8780" />
<Titles Title="Secrets of Silicon Valley" Price="20" Ytd_sales="4095" />
<Titles Title="Net Etiquette" />
<Titles Title="Computer Phobic AND Non-Phobic Individuals: Behavior Variations" Price="21.59" Ytd_sales="375" />
<Titles Title="Is Anger the Enemy?" Price="10.95" Ytd_sales="2045" />
<Titles Title="Life Without Fear" Price="7" Ytd_sales="111" />
<Titles Title="Prolonged Data Deprivation: Four Case Studies" Price="19.99" Ytd_sales="4072" />
<Titles Title="Emotional Security: A New Algorithm" Price="7.99" Ytd_sales="3336" />
<Titles Title="Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean" Price="20.95" Ytd_sales="375" />
<Titles Title="Fifty Years in Buckingham Palace Kitchens" Price="11.95" Ytd_sales="15096" />
<Titles Title="Sushi, Anyone?" Price="14.99" Ytd_sales="4095" />
</root>
Напишем следующий текст, и сохраним его
в файле primer.xsl
<?xml version="1.0" encoding="WINDOWS-1251"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
version="1.0">
<xsl:template match="/">
<Table border="1" cellpadding="0" id="fxTable" style="font: 8pt verdana" >
<!-- Печатаем названия столбцов - берем из первой строки -->
<xsl:for-each select="//*[1]/@*">
<!-- Выводим название аттрибута, т.е. название столбца -->
<th><xsl:value-of select="name()"/></th>
</xsl:for-each>
<!-- Применяем шаблон для всех элементов документа -->
<xsl:apply-templates select="//*"/>
</Table>
</xsl:template>
<xsl:template match="//*">
<!-- Если узел имеет родителя, т.е. отсекаем корневой элемент -->
<xsl:if test="parent::*">
<!-- Выводим строку таблицы -->
<tr>
<!-- Перебираем в строке все аттрибуты и выводим их значения -->
<xsl:for-each select="@*">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
В файл primer.aspx добавим инструкцию
cmd.XslPath = Server.MapPath("primer.xsl")
и закомментарим строку
Response.ContentType = "text/xml",
по умолчанию поток будет выводится в формате
HTML.
На экране видим следующую таблицу
Title |
Price |
Ytd_sales |
The Busy Executive's Database Guide |
19.99 |
4095 |
Cooking with Computers: Surreptitious
Balance Sheets |
11.95 |
3876 |
You Can Combat Computer Stress! |
2.99 |
18722 |
Straight Talk About Computers |
19.99 |
4095 |
Silicon Valley Gastronomic Treats |
19.99 |
2032 |
The Gourmet Microwave |
2.99 |
22246 |
The Psychology of Computer Cooking |
But Is It User Friendly? |
22.95 |
8780 |
Secrets of Silicon Valley |
20 |
4095 |
Net Etiquette |
Computer Phobic AND Non-Phobic Individuals:
Behavior Variations |
21.59 |
375 |
Is Anger the Enemy? |
10.95 |
2045 |
Life Without Fear |
7 |
111 |
Prolonged Data Deprivation: Four Case
Studies |
19.99 |
4072 |
Emotional Security: A New Algorithm |
7.99 |
3336 |
Onions, Leeks, and Garlic: Cooking Secrets
of the Mediterranean |
20.95 |
375 |
Fifty Years in Buckingham Palace
Kitchens |
11.95 |
15096 |
Sushi, Anyone? |
14.99 |
4095 |
Primer.xsl – универсальный шаблон,
который превращает XML-данные (при режиме AUTO)
в таблицу, с названиями столбцов равными
названиям полей. В общем, сам по себе пример не
очень ценный. Связка DataSource и DataGrid
делают все тоже самое. Ценность XML проявляется
при нестандартных ситуациях. Обратимся к примеру
из предыдущей статьи, где происходит вывод
публикаций в разрезе Издателей.
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Издатель Номер="0736">
<Книга Название="Emotional Security: A New Algorithm" Цена="7.99" Продажи="3336" />
<Книга Название="Is Anger the Enemy?" Цена="10.95" Продажи="2045" />
<Книга Название="Life Without Fear" Цена="7" Продажи="111" />
<Книга Название="Prolonged Data Deprivation: Four Case Studies" Цена="19.99" Продажи="4072" />
<Книга Название="You Can Combat Computer Stress!" Цена="2.99" Продажи="18722" />
</Издатель>
<Издатель Номер="0877">
<Книга Название="Computer Phobic AND Non-Phobic Individuals: Behavior Variations" Цена="21.59" Продажи="375" />
<Книга Название="Fifty Years in Buckingham Palace Kitchens" Цена="11.95" Продажи="15096" />
<Книга Название="Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean" Цена="20.95" Продажи="375" />
<Книга Название="Silicon Valley Gastronomic Treats" Цена="19.99" Продажи="2032" />
<Книга Название="Sushi, Anyone?" Цена="14.99" Продажи="4095" />
<Книга Название="The Gourmet Microwave" Цена="2.99" Продажи="22246" />
<Книга Название="The Psychology of Computer Cooking" />
</Издатель>
<Издатель Номер="1389">
<Книга Название="But Is It User Friendly?" Цена="22.95" Продажи="8780" />
<Книга Название="Cooking with Computers: Surreptitious Balance Sheets" Цена="11.95" Продажи="3876" />
<Книга Название="Net Etiquette" />
<Книга Название="Secrets of Silicon Valley" Цена="20" Продажи="4095" />
<Книга Название="Straight Talk About Computers" Цена="19.99" Продажи="4095" />
<Книга Название="The Busy Executive's Database Guide" Цена="19.99" Продажи="4095" />
</Издатель>
</root>
Если руководство захочет видеть информацию по
каждому издателю в отдельной таблице, то сразу
возникает вопрос – количество издателей (таблиц)
жестко не определено. Оно может меняться.
Конечно, можно динамически добавлять DataGrid’ы,
но на XML проще и кода меньше. Кстати, давайте
выведем эту информация в HTML-таблицы. Вот
шаблон.
<?xml version="1.0" encoding="WINDOWS-1251"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
version="1.0">
<xsl:template match="/">
<xsl:for-each select="//Издатель">
<h2>
<xsl:value-of select="@Номер"/>
</h2>
<Table border="1" cellpadding="3" id="fxTable" style="font: 8pt verdana" >
<xsl:for-each select="*[1]/@*">
<!-- Выводим название аттрибута, т.е. название столбца -->
<th><xsl:value-of select="name()"/></th>
</xsl:for-each>
<xsl:apply-templates select="Книга"/>
</Table>
<br/>
</xsl:for-each>
</xsl:template>
<xsl:template match="Книга">
<tr>
<!-- Перебираем в строке все аттрибуты и выводим их значения -->
<xsl:for-each select="@*">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Теперь при появлении нового издателя, не надо
переделывать ни код, ни шаблон. Автоматически
появится новая таблица с информацией.
0736
Название |
Цена |
Продажи |
Emotional Security: A New Algorithm |
7.99 |
3336 |
Is Anger the Enemy? |
10.95 |
2045 |
Life Without Fear |
7 |
111 |
Prolonged Data Deprivation: Four Case
Studies |
19.99 |
4072 |
You Can Combat Computer Stress! |
2.99 |
18722 |
0877
Название |
Цена |
Продажи |
Computer Phobic AND Non-Phobic Individuals:
Behavior Variations |
21.59 |
375 |
Fifty Years in Buckingham Palace
Kitchens |
11.95 |
15096 |
Onions, Leeks, and Garlic: Cooking Secrets
of the Mediterranean |
20.95 |
375 |
Silicon Valley Gastronomic Treats |
19.99 |
2032 |
Sushi, Anyone? |
14.99 |
4095 |
The Gourmet Microwave |
2.99 |
22246 |
The Psychology of Computer
Cooking |
1389
Название |
Цена |
Продажи |
But Is It User Friendly? |
22.95 |
8780 |
Cooking with Computers: Surreptitious
Balance Sheets |
11.95 |
3876 |
Net Etiquette |
Secrets of Silicon Valley |
20 |
4095 |
Straight Talk About Computers |
19.99 |
4095 |
The Busy Executive's Database Guide |
19.99 |
4095 |
Очень многих ASP.NET-программистов интересует
вопрос представления данных клиенту в виде
Excel-документа. Это дает клиенту возможность
дальнейшего манипулирования данными
самостоятельно, без участия программиста.
Как превратить XML-поток в XML-документ,
который распознается браузером как
Excel-таблица, покажем на примере того же
запроса в начале этой статьи.
Сразу скажу, что многие вопросы можно решить,
проделав элементарные действия. В Excel
отформатировать внешний вид документа (фонт,
цвет, рамки ячеек и пр.), затем сохранить в XML
виде, и проанализировать текст, что и как
происходит.
В файле primer.aspx заменим строку
Response.ContentType = "text/xml" на
Response.ContentType =
"application/vnd.ms-excel"
Следующий шаблон превратит наши данные в
браузере в Excel-таблицу
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/TR/REC-html40"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<xsl:template match="/">
<xsl:processing-instruction name=">
<xsl:text>progid="Excel.Sheet"</xsl:text>
</xsl:processing-instruction>
<Workbook>
<Worksheet ss:Name="Sheet 1">
<Table>
<Row>
<xsl:for-each select="//*[1]/@*">
<Cell>
<Data ss:Type="String"><xsl:value-of select="name()"/></Data>
</Cell>
</xsl:for-each>
</Row>
<xsl:apply-templates select="//*"/>
</Table>
</Worksheet>
</Workbook>
</xsl:template>
<xsl:template match="//*">
<xsl:if test="parent::*">
<Row>
<xsl:for-each select="@*">
<Cell><Data ss:Type="String"><xsl:value-of select="."/></Data></Cell>
</xsl:for-each>
</Row>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
У данного шаблона есть несколько недостатков. Первое, в нем нельзя вставлять
комментарии ( вида <!—ля-ля -->) и второе, все данные выводятся в текстовом
виде, т.е. ячейки, куда выводится числовая информация, будут отформатированы
как текст. Что бы исправить этот недостаток, надо изменить алгоритм, и привязаться
к анализу имен атрибутов. Теряется универсальность, но данные выводятся согласно их типу.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/TR/REC-html40"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<xsl:template match="/">
<xsl:processing-instruction name=">
<xsl:text>progid="Excel.Sheet"</xsl:text>
</xsl:processing-instruction>
<Workbook>
<Styles>
<Style ss:ID="s22"><NumberFormat ss:Format="Short Date"/></Style>
<Style ss:ID="s23"><NumberFormat ss:Format="Standard"/></Style>
</Styles>
<Worksheet ss:Name="Sheet 1">
<Table>
<Row>
<xsl:for-each select="//*[1]/@*">
<Cell>
<Data ss:Type="String">
<xsl:value-of select="name()"/>
</Data>
</Cell>
</xsl:for-each>
</Row>
<xsl:apply-templates select="//*"/>
</Table>
</Worksheet>
</Workbook>
</xsl:template>
<xsl:template match="//*">
<xsl:if test="parent::*">
<Row>
<xsl:for-each select="@*">
<xsl:choose>
<xsl:when test="name()='pub_id' or
name()='advance' or
name()='royalty' or
name()='ytd_sales'">
<Cell><Data ss:Type="Number"><xsl:value-of select="."/></Data></Cell>
</xsl:when>
<xsl:when test="name()='price'">
<Cell ss:StyleID="s23"><Data ss:Type="Number"><xsl:value-of select='.'/></Data></Cell>
</xsl:when>
<xsl:when test="name()='pubdate'">
<Cell ss:StyleID="s22"><Data ss:Type="DateTime"><xsl:value-of select="."/></Data></Cell>
</xsl:when>
<xsl:otherwise>
<Cell><Data ss:Type="String"><xsl:value-of select="."/></Data></Cell>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Row>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
В этом шаблоне применяется форматирование
ячеек, стили определяются в секции
<Styles>. Все тонкости применения стилей
форматирования можно найти на ссылке http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xmlss.asp
или с помощью сохранения таблицы с
отформатированными ячейками как XML-документ, и
дальнейшим поиском соответствующего стиля.
В общих чертах эта вся технология превращения
XML-потока либо HTML, либо в Excel-файл. Всё
дальнейшее совершенствование коснется только
внешнего вида (т.е. применение стилей
форматирования) или обработки XML-данных, а это
уже напрямую выводит на изучение языка XSLT.
В заключение статьи приведу координаты книги
по данному языку. Автор, один из сильнейших
специалистов в этой области (обработке XML).
Майкл Кей, XSLT. Спавочник
программиста. Пер. с англ.- СПб:Символ-Плюс,
2002.-1016 с., ил. ISBN
5-93286-039-1
Вот по этой ссылке http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_xl2003_ta/html/odc_XLxmlhowto_.asp
лежит теория и примеры связки ASP.old + XML =
Excel-таблица.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xmlss.asp
- справочник по SpeadSheet-
элементам.