вторник, ноября 08, 2005

О преобразованиях

Ещё один день закончился (как раз в то время, как у других он только начинается). Закончился он написанием шаблонов для двухступенчатых преобразований текстов. В общем и целом - ничего особенного. Однако для ряда случаев - очень полезна. Таковых случаев я знаю несколько, но все они сводятся к применению т.н. skins. В некотором смысле - высшая форма отделения представления от "логики". Говоря честно, мне по-английски такие тексты было бы писать проще, а то все эти кавычки... Ну так вот. Суть можно изложить в трёх предложениях:
1. Объект, моделирующий нечто, отдаёт свои данные в виде XML
2. По строке (а ведь всякий XML - строка, чем и ценится) строится метамодель представления (тоже в XML) путём применения к исходному XML XSLT
3. К метамодели применяется другой XSLT (а может, третий, четвёртый и т.п.), отвечающий за применение к метамодели skins.

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

Вкратце, механизм (С#, .NET 1.1)...
1. Класс, ответственный за что-то, должен уметь представить свои данные в XML


class BusinessClass{
int m_Id = 123;
string m_Name = "Name";
...
public string ToXml()
{
string s = "";
s += "<downloadupdate>";
s += "<id>";
s += m_Id.ToString();
s += "</id>";
s += String.Format("<name>{0}</name>", m_Name);
s += "</downloadupdate>";

return s;
}
}


2. Написать XSLT, который по XML-данным объекта формирует метамодель. То есть, например

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="yes"/>
<xsl:template match="downloadUpdate">
<line><xsl:apply-templates/></line>
</xsl:template>
<xsl:template match="downloadUpdate/id">
<cell><xsl:apply-templates/></cell>
</xsl:template>
<xsl:template match="downloadUpdate/name">
<cell><xsl:apply-templates/></cell>
</xsl:template>
</xsl:stylesheet>

3. Написать ещё один XSLT, который применял бы к метамодели skin.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="yes"/>
<xsl:template match="line">
<HTML>
<BODY>
<TABLE>
<TBODY>
<TR>
<xsl:apply-templates/>
</TR>
</TBODY>
</TABLE>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="cell">
<TD>
<xsl:apply-templates/>
</TD>
</xsl:template>
</xsl:stylesheet>


4. Это почти всё. Трансформации, КГБычно, выполняются примерно так.

XmlDocument doc = new XmlDocument();
doc.LoadXml(object.ToXml());

XPathNavigator nav = doc.DocumentElemen.CreateNavigator();

MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8);
XslTransform xt = new XslTransform();
xt.Load(PATH_TO_XSLT_1);
xt.Transform(nav, null, writer, null);

ms.Seek(0, SeekOrigin.Begin);
byte[] b = new byte[ms.Length];

ms.Read(b, 0, (int)ms.Length);
ms.SetLength(0);

string s = Encoding.UTF8.GetString(b);

Это первый шаг преобразования, после него получаем что-то вроде

<?xml version="1.0" encoding="utf-8"?>
<line>
<cell>123</cell>
<cell>Name</cell>
</line>


На втором шаге надо к строке s применить то же самое, что и к исходному XML, а потом выбросить результат в Response. Из побочных эхвектов я отмечаю большую нагрузку на процессор, что надо иметь в виду.

Может, как-нибудь напишу, как генерацию представления правильно совместить с серверными элементами управления. Но уж не сегодня.

Комментариев нет: