Zpracovßnφ databßzov²ch dat p°es XSLT v ASP
Ka₧d² ASP programßtor jist∞ znß obvykl² zp∙sob psanφ webov²ch databßzov²ch aplikacφ, p°i kterΘm se p°ipojφ k databßzi, spuÜt∞nφm dotazu zφskß data v Recordsetu, iteracφ je prochßzφ, zpracovßvß a slepuje po₧adovan² HTML k≤d. Modernφ doba vÜak po₧aduje trochu nov∞jÜφ a hlavn∞ robustn∞jÜφ p°φstup k prßci s daty za vyu₧itφ nov²ch prost°edk∙, jako jsou XML a XSLT. Jak uvidφte, tyto postupy jsou p°φstupnΘ i na dnes ji₧ p°e₧itΘ platform∞ ASP.
Tento Φlßnek voln∞ navazuje na p°edchozφ XmlDataDocument a zobrazenφ stromovΘ struktury p°es XSLT v ASP.NET, ve kterΘm jsme °eÜili ·kol podobn², tentokrßt vÜak aplikaci postavφme na trochu starÜφ platform∞. Struktura databßzovΘ tabulky je stßle stejnß - je rozebrßna v p∙vodnφm Φlßnku, proto se k nφ nebudu vracet, mφsto toho se hned vrhneme na k≤d naÜφ aplikace, p°iΦem₧ budu poukazovat na nejd∙le₧it∞jÜφ rozdφly obou °eÜenφ.
NaΦtenφ vstupnφch dat
Prvnφm krokem je samoz°ejm∞ op∞t zφskßnφ dat, se kter²mi pak budeme pracovat. To znamenß p°ipojenφ k databßzi, vyvolßnφ dotazu a p°evedenφ dat do XML formßtu, kter² pot°ebujeme pro XSL transformaci. Jeliko₧ vÜak v ASP nemßme k dispozici takov² luxus jak² nabφzφ v .NETu t°φda XmlDataDocument
, musφme najφt jin² zp∙sob zφskßnφ XML dokumentu z tabulky. Nebudeme vym²Ület ₧ßdnΘ krkolomnΘ zp∙soby a vyu₧ijeme komponentu SQLXML, kterß by nem∞la b²t na Microsoft SQL Serveru 2000 (a nov∞jÜφch) ₧ßdn² problΘm. OvÜem nep°edbφhejme!
K p°φstupu k dat∙m pou₧ijeme ADO a standardnφ OLEDB ovladaΦ. Komponenta SQLXML sice obsahuje takΘ specißlnφho poskytovatele SQLXMLOLEDB, kter² vlastn∞ pracuje nad p∙vodnφm SQLOLEDB (viz Architecture of Client-Side and Server-Side XML Formatting), ovÜem pokud mßte webov² server odd∞len² od databßzovΘho, pravd∞podobn∞ tohoto poskytovatele k dispozici nemßte. To vÜak nevadφ, proto₧e s b∞₧n²m OLEDB si bez problΘm∙ vystaΦφme.
Dotaz pro databßzi bude obyΦejn² SELECT s dov∞tkem FOR XML AUTO, kter²m po₧ßdßme SQL server, aby nßm vrßtil v²sledek v XML formßtu.
dim conn
set conn = Server.CreateObject("ADODB.Connection")
conn.Open "Provider=SQLOLEDB; Data Source=***; Initial Catalog=***; UId=***; Pwd=***"
dim cmd
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = "SELECT * FROM diskuse FOR XML AUTO"
Jak bude vypadat v²sledek takovΘho dotazu? M∙₧ete si to vyzkouÜet v Query Analyzeru, pokud jej mßte k dispozici. Zpracovßnφm dotazu vznikne fragment XML dokumentu jako dlouh² °et∞zec, kter² je pak rozd∞len po 256 znacφch do n∞kolika °ßdek jakΘsi fiktivnφ tabulky s jedin²m sloupcem. Zde ovÜem nastßvß menÜφ problΘm. Sloupec toti₧ nenφ °et∞zcovΘho typu (String), n²br₧ jsou to binßrnφ data, kterß jsou ve VBS reprezentovßny jako pole bajt∙ - Byte().
S tφmto datov²m typem se ve VBS velmi obtφ₧n∞ pracuje, museli bychom tedy ka₧d² °ßdek n∞jak²m zp∙sobem p°evΘst na °et∞zec a pak vÜechny °ßdky spojit dohromady. Existuje vÜak jednoduÜÜφ a takΘ v²konn∞jÜφ zp∙sob. V²sledek dotazu lze cel² najednou "nasypat" do takzvanΘho streamu a pak jej cel² p°eΦφst jako °et∞zec, se kter²m ji₧ lze snadno pracovat. Nejd°φve tedy musφme stream vytvo°it, otev°φt a propojit jej s naÜim objektem cmd.
set stream = Server.CreateObject("ADODB.Stream")
stream.Open
cmd.Properties("Output Stream").Value = stream
VÜechno je p°ipraveno, tak₧e m∙₧eme p°φkaz spustit. P°itom musφme poskytovateli °φci, aby v²slednß data zapsal do p°ipojenΘho streamu, k Φemu₧ slou₧φ t°etφ parametr metody Execute, kterΘmu nastavφme hodnotu ADO konstanty adExecuteStream, tedy 1024. Ostatnφ parametry mohou z∙stat prßzdnΘ. PotΘ okam₧it∞ uzav°eme databßzovΘ p°ipojenφ, abychom zbyteΦn∞ neblokovali zdroje SQL serveru ostatnφm proces∙m.
conn.close
Nynφ stream obsahuje kompletnφ v²sledek dotazu. P°esuneme se na jeho zaΦßtek, a naΦteme cel² jeho obsah jako °et∞zec do novΘho XML dokumentu. P°itom musφme jeho obsah uzav°φt do jedinΘho elementu, proto₧e se jednß o XML fragment, kter² m∙₧e obsahovat vφce XML element∙, avÜak XML dokument smφ mφt jen jeden ko°enov² element. NßÜ element si pojmenujeme p°φznaΦn∞ diskuse. K dobr²m mrav∙m pat°φ takΘ p°ipojenφ XML deklarace.
set xml = Server.CreateObject("MSXML2.DOMDocument")
stream.Position = 0
xml.LoadXML("<?xml version='1.0'?>" & _
"<diskuse>" & _
stream.ReadText & _
"</diskuse>")
Je t°eba dßt pozor na chyby MS XML parseru, kter² nevyvolßvß v²jφmky, pouze si zapisuje p°φpadnΘ chyby do vlastnosti parseError dokumentu. Kdyby doÜlo k chyb∞, b∞h k≤du pokraΦuje normßln∞ dßle a chyba se projevφ n∞kde jinde, ovÜem pak se nßm m∙₧e skuteΦn² zdroj chyby hledat velice t∞₧ko. Proto tΘto situaci rad∞ji p°edejdeme a p°φpadnou chybu zpracujeme ihned na mφst∞ - v ukßzkovΘ aplikaci pouze vypφÜeme chybovou zprßvu a ukonΦφme skript.
response.write "Error loading XML: " & _
xml.parseError.reason
response.end
end if
Dlu₧no dodat, ₧e pravd∞podobnost chyby parsingu je asi pom∞rn∞ nφzkß, proto₧e onen XML dokument nikdo "nelepφ ruΦn∞", ale je generovßn komponentou SQLXML, p°iΦem₧ lze jist∞ ·sp∞Ün∞ pochybovat o tom, ₧e by tato komponenta vytvß°ela nevalidnφ XML. OvÜem rozmary Microsoft produkt∙ nenφ nikdy dobrΘ podce≥ovat a kdy₧ u₧ nic jinΘho, alespo≥ testujeme, zda jsme v²sledek dotazu sprßvn∞ uzav°eli do ko°enovΘho elementu dokumentu.
XML dokument je p°ipraven. Jeho vnit°nφ strukturou jsme se zatφm nezab²vali, poj∩me to napravit. Budeme ji muset dob°e znßt, abychom pak sestavili dob°e fungujφcφ XSL transformaci. Zp∙sob, jak²m generuje server XML data, zßvisφ Φist∞ na pou₧itΘm dotazu. My jsme pou₧ili jeden ze zßkladnφch tvar∙ FOR XML AUTO a v takovΘm p°φpad∞ zφskßme dokument podobn² nßsledujφcφmu:
<diskuse>
<diskuse
ID="1"
autor="paya"
obsah="4all: Ahoj, jdeme na pivo?" />
<diskuse
ID="2"
pID="1"
autor="pierre"
email="ja@pierre.cz"
obsah="OK, v 19h na Stodolnφ?" />
...
</diskuse>
V²slednß struktura je trochu odliÜnß od tΘ, kterß vznikß zpracovßnφm .NET t°φdou XmlDataDocument. Ka₧d² °ßdek tabulky se p°evede na XML element, jeho₧ nßzev je shodn² s nßzvem p∙vodnφ tabulky. Nejviditeln∞jÜφ zm∞nou je p°evod sloupc∙ na XML atributy mφsto XML element∙. NicmΘn∞ pokud bychom tou₧ili vφce po XML elementech, staΦφ zm∞nit dopln∞k naÜeho SQL dotazu na FOR XML AUTO, ELEMENTS. To vÜak jen tak na okraj. VÜimn∞te si, ₧e pole, kterß majφ v databßzovΘ tabulce hodnotu NULL, nejsou v dokumentu op∞t v∙bec vid∞t.
Dokument jsme si prohlΘdli, zb²vß jen naΦφst stylesheet provΘst transformaci a v²sledek vypustit na prohlφ₧eΦ klienta:
set xsl = Server.CreateObject("MSXML2.DOMDocument")
xsl.load(server.MapPath("diskuse.xsl"))
response.write xml.transformNode(xsl)
%>
No a ·pln∞ nakonec napφÜeme stylesheet. Respektive staΦφ pou₧φt ukßzku z p∙vodnφho Φlßnku a upravit ji pro zpracovßnφ XML dokumentu s naÜφ mφrn∞ odliÜnou strukturou. FunkΦnost a v²sledek transformace z∙stanou naprosto toto₧nΘ.
<xsl:stylesheet
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
version='1.0'>
<xsl:template match="/">
<ul>
<xsl:apply-templates
select="diskuse/diskuse[count(pID)=0]" />
</ul>
</xsl:template>
<xsl:template match="diskuse">
<li>
<h4>
<xsl:value-of select="@autor" />
<xsl:if test="@email">
<xsl:text> (</xsl:text>
<a href="mailto:{@email}">
<xsl:value-of select="@email" />
</a>
<xsl:text>)</xsl:text>
</xsl:if>
</h4>
<p><xsl:value-of select="@obsah" /></p>
<hr/>
</li>
<xsl:if test="/diskuse/diskuse[@pID=current()/@ID]">
<ul>
<xsl:apply-templates
select="/diskuse/diskuse[@pID=current()/@ID]"/>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
K dispozici je vßm i ukßzkovß aplikace ke sta₧enφ.
Odkazy, zdroje
- XmlDataDocument a zobrazenφ stromovΘ struktury p°es XSLT v ASP.NET - B°φza, Petr (Interval.cz, 12. 7. 2004)
- Generujeme XML z MS SQL Serveru - B°φza, Petr (Interval.cz, 1. 4. 2004)
- SQLXML (MSDN library, nedatovßno)
- Kompletnφ pr∙vodce XSLT - B°φza, Petr (Interval.cz, nedatovßno)
- XSLT v p°φkladech - Ji°φ Kosek (www.kosek.cz, 26. 4. 2004)
- The Extensible Stylesheet Language Family (XSL) (W3C, nedatovßno)