Симон Робинсон - C# для профессионалов. Том II
// Выполнить преобразование. Файл вывода создается здесь.
transForm.Transform(nav, null, fs);
}
Сделать это преобразование проще почти невозможно. Сначала создается объект на основе XPathDocument и объект на основе XslTransform. Затем файл bookspath.xml загружается в doc, a books.xsl в transForm. В этом примере для записи нового документа HTML на диск создается объект FileStream.
Если бы это было приложение ASP.NET, мы использовали бы объект TextWriter и передавали бы его в объект HttpResponse. Если бы мы преобразовывали в другой документ XML, то применялся бы объект на основе XmlWriter. После того как объекты XPathDocument и XslTransform будут готовы, мы создаем XPathNavigator на doc и передаем nav и этот stream в метод Transform объекта transForm. XslTransform имеет несколько перегружаемых версий, получающих комбинации навигаторов, XsltArgumentList (подробнее об этом позже) и потоков ввода/вывода. Параметром навигатора может быть XPathNavigator или любой объект, реализующий интерфейс IXPathNavigable. Потоки ввода/вывода могут быть TextWriter, Stream или объектом на основе XmlWriter.
Документ books.xsl является таблицей стилей. Документ выглядит следующим образом:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>Price List</title>
</head>
<body>
<table>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="bookstore">
<xsl:apply-templates select= "book"/>
</xsl:template>
<xsl:template match="book">
<tr><td>
<xsl:value-of select="title"/>
</td><td>
<xsl:value-of select="price"/>
</td></tr>
</xsl:template>
</xsl:stylesheet>
Ранее упоминался объект XsltArgumentList. Это способ, которым можно объект с методами связать с пространством имен. Когда это сделано, можно вызывать методы во время преобразования. Рассмотрим пример, чтобы понять, как это работает (находится в XPathXSLSample4):
private void button1_Click(object sender, System.EventArgs e) {
// новый XPathDocument
XPathDocument doc=new XPathDocument("..\..\..\booksxpath.xml");
// новый XslTransform
XslTransform transForm=new XslTransform();
transForm.Load("..\..\..\booksarg.xsl");
// новый XmlTextWriter, так как мы создаем новый документ xml
XmlWriter xw=new XmlTextWriter(..\..\..\argSample.xml", null);
// создать XslArgumentList и новый объект BookUtils
XsltArgumentList argBook=new XsltArgumentList();
BookUtils bu=new BookUtils();
// это сообщает список аргументов BookUtils
argBook.AddExtensionObject("urn:ProCSharp", bu);
// новый XPathNavigator
XPathNavigator nav=((IXPathNavigable)doc).CreateNavigator();
// выполнить преобразование
transForm.Transform(nav, argBook, xw);
xw.Close();
}
// простой тестовый класс
public class BookUtils {
public BookUtils() {}
public string ShowText() {
return "This came from the ShowText method!";
}
}
Вывод преобразования (argSample.xml) выглядит так:
<?xml version="1.0"?>
<books>
<discbook>
<booktitle>The Autobiography of Benjamin Franklin</booktitle>
<showtext>This came from the ShowText method!</showLext>
</discbook>
<discbook>
<booktitle>The Confidence Man</booktitle>
<showtext>This came from the ShowText method!</showtext>
</discbook>
<discbook>
<booktitle>The Gorgias</booktitle>
<showtext>This came from the ShowText method!</showtext>
</discbook>
<discbook>
<booktitle>The Great Cookie Caper</booktitle>
<showtext>This came from the ShowText method!</showtext>
</discbook>
<discbook>
<booktitle>A Really Great Book</booktitle>
<showtext>This came from the ShowText method!</showtext>
</discbook>
</books>
Определим новый класс BookUtils. В этом классе мы имеем один практически бесполезный метод, который возвращает строку "This came from the ShowText method!". Для события button1_Click создаются XPathDocument и XslTransform так же, как это делалось раньше, но с некоторыми исключениями. В этот раз мы собираемся создать документ XML, поэтому используем XMLWriter вместо FileStream. Вот эти изменения:
XsltArgumentList argBook=new XsltArgumentList();
BookUtils bu=new BookUtils();
argBook.AddExtensionObject("urn:ProCSharp", bu);
Именно здесь создается XsltArgumentList. Мы создаем экземпляр объекта BookUtils, и когда вызывается метод AddExtensionObject, ему передается пространство имен расширения и объект, из которого мы хотим вызывать методы. Когда делается вызов Transform, ему передаются XsltArgumentList (argBook) вместе с XPathNavigator и созданный объект XmlWriter. Вот документ booksarg.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:bookutil="urn:ProCSharp">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="books">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="bookstore">
<xsl:apply-templates select="book"/>
</xsl:template>
<xsl:template match="book">
<xsl:element name="discbook">
<xsl:element name="booktitle">
<xsl:value-of select="title"/>
</xsl:element>
<xsl:element name="showtext">
<xsl:value-of select="bookUtil:ShowText()"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Здесь имеются две важные строки. В начале добавляется пространство имен, которое создается при добавлении объекта к XsltArgumentList. Затем применяется стандартный синтаксис использования префикса перед пространством имен XSLT и вызывается метод.
Иначе это можно было бы выполнить с помощью сценария XSLT. В таблицу стилей можно включить код C#, VB и JavaScript. Большим достоинством этого является то, что в отличие от текущих реализаций, сценарий компилируется при вызове Transform.Load; таким образом выполняются уже откомпилированные сценарии, в значительной степени так же, как работает ASP.NET. Давайте выполним предыдущий пример таким способом. Добавим сценарий к таблице стилей. Эти изменения можно увидеть в файле bookscript.xsl:
<xsl:stylesheet version="1.0" xmlns:Xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="http://wrox.com">
<msxsl:script language="C#" implements-prefix="user">
string ShowText() {
return "This came from the ShowText method!";
}
</msxsl:script>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="books">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="bookstore">
<xsl:apply-templates select="book"/>
</xsl:template>
<xsl:template match="book">
<xsl:element name="discbook">
<xsl:element name="booktitle">
<xsl:value-of select="title"/>
</xsl:element>
<xsl:element name="showtext">
<xsl:value-of select="user:ShowText()"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Изменения включают задание пространства имен сценариев, добавление кода (который скопирован из VS.NET IDE) и выполнение вызова в таблице стилей. Вывод выглядит так же, как и в предыдущем примере.
Ключевой момент, о котором необходимо помнить при выполнении преобразований, состоит в том, чтобы не забыть использовать подходящее хранилище; XPathDocument, если не требуется редактирование, XmlDataDocument, если данные получают из ADO.NET, и XmlDocument, если необходимо иметь возможность редактировать данные. Процесс будет таким же, несмотря ни на что.
XML и ADO.NET
XML является средством, которое связывает ADO.NET с остальным миром. ADO.NET был создан для работы внутри среды XML. XML используется для преобразования данных в и из хранилища данных в приложение или страницу Web. Так как ADO.NET использует XML в качестве транспорта, то данными можно обмениваться с приложениями и системами, которые даже не знают об ADO.NET. Пока обрабатывается XML, они могут совместно использовать данные. ADO.NET может читать документы XML, возвращаемые из этих же приложений. В связи с важностью XML для ADO.NET, существует ряд полезных свойств ADO.NET, которые позволяют чтение и запись документов XML. Пространство имен XML содержит также классы, которые могут потреблять или утилизировать реляционные данные ADO.NET.