Navigace

Hlavnφ menu

 

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.

dim stream
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.

cmd.Execute , , 1024
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.

dim xml
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.

if xml.parseError.errorCode <> 0 then
  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:

<?xml version="1.0"?>
<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:

dim xsl
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Θ.

<?xml version='1.0'?>
<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

B°φza, Petr (29. 7. 2004)