Introduction
Avec Delphi il est possible de développer
des applications web fondées sur l'architecture XML/XSL que
je présente dans cet article.
Kylix 2 apporte lui aussi le framework WebSnap permettant de
faciliter l'écriture d'application orientée web. Seulement
voilà, le hic c'est que les composants XMLBroker et
XSLPageProducer n'apparaissent pas dans la palette de
composants de Kylix 2 ! Ceci vient du fait Borland n'as pas
interfacé de processeur XSL pour le composant TXMLDocument.
Delphi 6 s'appuie sur le processeur de Microsoft
(msxml3.dll) qui n'est bien sûr pas disponible sous Linux.
Il existe cependant des processeurs XSL sous linux: Celui
d'Oracle mais ne gérant pas le XPath, celui de Xalan
(écrit en C++), libxslt
qui est une bibliothèque pour Gnome et enfin Sablotron
que nous allons utiliser dans cet article.
Tous n'implémentent pas forcément entièrement la norme XSL
du W3C mais peuvent être suffisant pour faire les
transformations de bases.
Dans cet article, je vais montrer comment
développer une application web XML/XSL avec WebSnap
similaire a ce que l'on peut faire avec Delphi 6. Les
composant XMLBroker et XSLPageProducer ne sont pas disponibles dans la
palette de composants mais le code source est fourni. On
mettra aussi en évidence un problème de parseur XML
concernant ce composant.
Remarque: On utilisera ce qui a été
vu dans l'article
concernant le composant TXMLDocument.
Le composant XMLBroker
Dans la version Delphi de l'application
WebSnap/XSL, on utilise le composant XMLBroker pour
transformer les données extraites de la base de données en
un flux XML. Ce composant n'est pas enregistré
dans la palette de composant de Kylix 2, mais le code source
est fourni (source/internet/XMLBrokr.pas). Il suffit donc de
l'enregistrer comme si c'était notre propre composant:
unit
XMLBrokReg;
interface
procedure Register;
implementation
uses
Classes, XMLBrokr;
procedure Register;
begin
RegisterComponents('WebSnap', [TXMLBroker]);
end;
end. |
Puis d'ajouter ce fichier (XMLBrokReg.pas)
dans un paquet de composant et de l'installer. Le composant
TXMLBroker apparaîtra alors dans l'onglet WebSnap.
Le composant XSLPageProducer
Pour le composant TXSLPageProducer, ce n'est
pas aussi simple que pour le TXMLBroker. En effet si on
enregistre de la même façon ce composant on va tomber sur
un problème lié au code du composant. Le TXSLPageProducer
dérive du composant TXMLDocument et ajoute des méthodes
permettant de transformer un flux XML avec un fichier XSL.
Le composant charge dans sa structure interne (TXMLDocument)
le fichier XSL qu'on lui a associé. Puis lors de l'appel
aux méthodes de transformations, une nouveau TXMLDocument
est créé s'occupant de parser le flux XML. Voici le code
extrait du fichier XSLProd.pas (source/internet/XSLProd.pas):
function
TCustomXSLPageProducer.GetXMLDocument(out AXMLDocument: IXMLDocument): Boolean;
var
Stream: TStream;
Owned: Boolean;
begin
Result := Supports(IUnknown(XMLData),
IXMLDocument, AXMLDocument);
if Result then
if (csDesigning in ComponentState)
and (AXMLDocument.FileName <> '') then
begin
// Load the file when designing
Result := False;
AXMLDocument := nil;
end;
if not Result then
begin
Owned := False;
Stream := GetXMLData(Owned);
if Stream <> nil
then
try
AXMLDocument :=
TXMLDocument.Create('');
try
AXMLDocument.LoadFromStream(Stream);
except
FreeAndNil(AXMLDocument);
raise;
end;
Result := AXMLDocument <>
nil;
finally
if Owned
then
Stream.Free;
end;
end;
end; |
Le composant TXMLDocument est créé dynamiquement, mais
le DOMVendor n'est pas indiqué. Ceci a pour conséquence
que le flux XML sera traité par le parseur par défaut (variable
DefaultDOMVendor. MSXML sous windows, OpenXML sous Linux) ce qui dans notre cas sera OpenXML. Malheureusement
OpenXML ne gère pas le XSL et lors de la transformation, si
on a indiqué Sablotron comme parseur pour le
TXSLPageProducer, une erreur va survenir car il y aura
mélange des noeuds traités par OpenXML et ceux de Sablotron.
Il existe 2 solutions pour résoudre ce
problème. La première consiste à modifier la variable
globale DefaultDOMVendor définie et initialisée dans
xmldom.pas. On pourra par exemple dans le OnCreate du
WebPageModule principal de l'application écrire le code
suivant:
procedure
TListDepartment.WebPageModuleBeforeDispatchPage(Sender:
TObject;
const PageName: String; var
Handled: Boolean);
begin
DefaultDOMVendor := SSablot;
end; |
La constante SSablot est définie dans
l'unité sablotxmldom.pas. Mais cette solution ne peux
fonctionner qu'à l'exécution. Impossible de voir le
résultat à la conception.
La deuxième solution réside dans
l'écriture d'un nouveau composant descendant de
TXSLPageProducer. Il va ainsi être possible de corriger la
méthode GetXMLDocument posant problème. Cette méthode
n'est pas virtuelle donc impossible à redéfinir
directement. Mais elle est appelée par ContentFromStream
qui, par contre, est virtuelle. On va donc créer une
nouvelle méthode GetXMLDocumentEx qui va être appelé par
la méthode ContentfromStream que l'on aura redéfinie.
type
TXSLPageProducerEx = class(TXSLPageProducer, IProduceContent, IProduceContentFrom,
ISetAppDispatcher, IGetAppDispatcher, IProducerEditorViewSupport,
IGetProducerTemplate)
private
protected
function GetXMLDocumentEx(out AXMLDocument: IXMLDocument): Boolean;
procedure Loaded; override;
public
function ContentFromStream(InStream: TStream):
string; override;
published
end; |
function TXSLPageProducerEx.ContentFromStream(InStream: TStream):
string;
var
XMLDocument: IXMLDocument;
W: WideString;
begin
if XMLData = nil then
RaiseNoXMLData;
if GetXMLDocumentEx(XMLDocument)
then
begin
Self.LoadFromStream(InStream);
XMLDocument.Active := True;
if Self.DocumentElement = nil
then
RaiseNoXMLDocument
else if XMLDocument.Node =
nil then
RaiseNoXMLData;
XMLDocument.DocumentElement.TransformNode(Self.DocumentElement, W);
Result := W;
end
else
RaiseNoXMLDocument;
end;
function TXSLPageProducerEx.GetXMLDocumentEx(
out AXMLDocument: IXMLDocument): Boolean;
var
Stream: TStream;
Owned: Boolean;
XMLCompo: TXMLDocument;
begin
Result := Supports(IUnknown(XMLData), IXMLDocument, AXMLDocument);
if Result then
if (csDesigning in ComponentState)
and (AXMLDocument.FileName <> '') then
begin
// Load the file when designing
Result := False;
AXMLDocument := nil;
end;
if not Result then
begin
Owned := False;
Stream := GetXMLData(Owned);
if Stream <> nil
then
try
XMLCompo := TXMLDocument.Create('');
try
XMLCompo.DOMVendor := DOMVendor;
AXMLDocument := XMLCompo;
AXMLDocument.LoadFromStream(Stream);
except
FreeAndNil(AXMLDocument);
raise;
end;
Result := AXMLDocument <>
nil;
finally
if Owned
then
Stream.Free;
end;
end;
end; |
Rajoutez le fichier XSLPageProducerEx.pas
dans un paquet de composants (par exemple avec le
XMLBrokReg.pas) et notre nouveau composant apparaîtra dans
l'onglet WebSnap. Les modifications apportées à ce nouveau
composant permettent de synchroniser le DOMVendor pour le
fichier XSL et pour le flux XML. On pourra ainsi avoir la
prévisualisation à la conception.
Application WebSnap XML/XSL
Il nous est maintenant possible de réaliser
notre application XML/XSL avec WebSnap similaire
à celle
que l'on a pu faire avec Delphi 6. La différence réside
essentiellement dans l'emploi de dbExpress pour l'accès à
la base de données (employee.gdb d'Interbase).

Pour les WebPageModules (ListDepartment_pgm
et FormDepartment_pgm) il faut remplacer le XSLPageProducer
qui est mis par défaut par notre XSLPageProducerEx. La
propriété PageProducer du WebPageModule doit être mis à
jour pour pointer vers le nouveau composant. Enfin on
positionne le DOMVendor sur Sablotron.

Vous pouvez reprendre les fichiers XSL de
votre application Delphi 6, ce sont les mêmes ! Vous
constaterez alors que la transformation s'effectue
parfaitement dans la fenêtre de prévisualisation:


Conclusion
Nous avons vu qu'il était possible avec
Kylix 2, comme avec Delphi 6, de créer des applications web
XML/XSL en utilisant WebSnap. Même si cela n'est pas
évident de prime abord, une fois les bons composants
enregistrés, c'est comme sous windows. Le plus important
reste l'interfaçage avec un parseur qui gère le XSL. pour
plus d'information sur le sujet, je vous renvoie à mon article
sur le composant TXMLDocument
Voici les
fichiers XMLBrokReg.pas et XSLPageProducerEx.pas permettant
d'enregistrer les composants nécessaires au développement
d'applications web XML/XSL avec Kylix 2, et l'interface
de Sablotron pour le TXMLDocument.
|