Zpracování databázových dat přes XSLT v ASP

29. července 2004

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

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *