Ladate
IntroductionKylix 2 et Delphi 6 introduisent un nouveau composant TXMLDocument permettant de manipuler facilement les documents au format XML. La particularité de ce composant est sa capacité à utiliser différents parseurs XML. Sous Windows avec Delphi 6 c'est le parseur de Microsoft (MSXML) qui est utilisé par défaut, mais il est possible de changer pour celui d'IBM, d'OpenXML (entièrement écrit en Delphi), etc. Avec Kylix 2 c'est ce dernier qui est utilisé par défaut. Malheureusement, contrairement à celui de Microsoft, OpenXML n'implémente pas de transformation XSL ni de XPath. Mais avec l'architecture du composant TXMLDocument il est possible d'écrire une interface pour d'autre parseur tel que GDOM/libXSL (Gnome) ou Sablotron fournissant un processeur XSL et une gestion de XPath. L'objectif de cet article est de décrire l'architecture du composant TXMLDocument et de montrer comment implémenter une interface pour un parseur tiers. Remarque: Ceci est aussi valable pour Delphi 6, mais MSXML suffit largement pour les manipulations XML/XSL, alors qu'OpenXML fournit avec Kylix 2 n'implémente que le DOM.
Architecture de TXMLDocumentModèle objetLe composant TXMLDocument a la capacité de changer dynamiquement de parseur. Ceci est possible grâce à la définition dans l'architecture du composant d'une couche d'abstraction représentant les méthodes de manipulation du DOM. Cette couche d'abstraction (dans le fichier xmldom.pas) est entièrement faite à base d'interfaces(IInterface ou IUnknown). Pour rappel, les interfaces peuvent être comparées à des classes abstraites pures, c'est-à-dire que seules les méthodes sont déclarées et aucune implémentation n'est donnée. A partir de ces interfaces, on peut coder un comportement différent dans des classes les implémentant pour chacunes des méthodes définies. Chaque unité de parseur (msxmldom.pas, oxmldom.pas) implémente donc les interfaces du DOM et fait référence aux objets interne du parseur: Pour MSXML, le TXMLNode a une référence sur un IDOMNode (interface du DOM) qui est implémenté par TMSDOMNode (msxmldom.pas). Cette classe garde un référence su l'interface IXMLDOMNode qui est la représentation mémoire du noeud parsé par msxml3.dll. pour OpenXML, c'est la classe TOXDOMNode (oxmldom.pas) qui implémente le IDOMNode (interface du DOM). Cette classe a une référence du un TdomNode (XDom.pas) qui est la classe encapsulant le noeud pour le parseur OpenXML. Remarque: En tout, on a 3 objets en mémoire pour chaque noeud. Il faut donc garder à l'esprit que pour 1000 noeuds XML, on a 3000 objets en mémoire... Choix du parseurLe changement dynamique du parseur s'effectue via la propriété DOMVendor de type TDOMVendor. cette classe abstraite est déclarée de la façon suivante:
Dans la section initialization de chaque unité implémentant l'interfaçage pour chaque parseur, l'appel à la méthode RegisterDOMVendor permet d'enregistrer le nouveau parseur dans une liste interne globale (xmldom.pas) de tous les Vendors disponibles.
Dans les exemples précédents, TMSDOMImplementationFactory et TOXDOMImplementationFactory dérivent de TDOMVendor et redéfinissent les méthodes Description et DOMImplementation qui vont être appelées par le composant TXMLDocument. La méthode Description renvoie une chaîne de caractères indiquant le nom du parseur (MSXML, OpenXML, etc.). La méthode DOMImplementation renvoie une référence sur une classe implémentant l'interface IDOMImplementation fournissant entre autre la méthode CreateDocument, point d'entré du document XML.
Ajout d'un parseur tiersImplémentation des interfacesL'ajout d'un nouveau parseur au composant TXMLDocument passe par l'implémentation des interfaces décrites dans xmldom.pas. les plus importantes sont
On va donc créer une unité semblable à msxmldom.pas ou oxmldom.pas. Pour Sablotron je l'ai appelé sablotxmldom.pas. Il s'agit donc de créer des classes implémentant spécifiquement le comportement de l'appel aux méthodes du parseur sous jacent. Par exemple si l'on crée la classe TSablotDOMImplementation implémentant IDOMImplementation:
on aura le code de createDocument:
La donnée membre FSablotSituation est spécifique à l'implémentation de Sablotron. C'est une sorte de contexte permettant d'identifier sur quel document on est en train de travailler. Il est nécessaire pour tous les appels aux fonctions du parseur. La fonction SablotCreateDocument est une fonction de la bibliothèque Sablotron. Cette dernière est interfacée dans l'unité Sablot.pas où l'on retrouve les prototypes des fonctions publiés par le .so (ou la dll). TSablotDOMDocument est une classe qui implémente IDOMDocument. Un problème se pose: comment adapter un objet TSDOM_Node qui est un noeud Sablotron en une référence sur l'interface IDOMNode ? On va écrire des fonctions utilitaires permettant de générer les noeuds dans la représentation du DOM (Réference sur les interfaces du xmldom.pas). On a par exemple la fonction MakeNode qui à partir d'un node dans la représentation du parseur crée une référence sur IDOMNode. De même on fera un MakeNodeList pour adapter un TSDOM_NodeList en IDOMNodeList.
La fonction déclare un tableau de toutes les classes implémentant un descendant de IDOMNode. Après avoir récupéré le type du noeud passé en paramètre, on crée une instance grâce à la référence de classe correspondante. On va pouvoir ensuite l'utiliser de la manière suivante:
la fonction du parseur SDOM_CreateElement crée un nouveau noeud Sablotron (TSDOM_Node) puis on crée à partir de celui-ci un TSablotDOMElement implémentant l'interface IDOMElement du DOM. Enregistrement et utilisation du nouveau parseurPour rendre disponible ce nouveau parseur au niveau de TXMLDocument, il suffit de rajouter dans la clause initialization une RegisterDOMVendor avec l'instance de classe dérivante de TDOMVendor est redéfinissant les méthodes Description et DOMImplementation.
Dans l'exemple ci-dessus, TSablotDOMImplementationFactory dérive de TDOMVendor. la méthode DOMImplementation doit renvoyer une référence sur IDOMImplemention. il suffit donc d'instancier la classe l'implémentant:
Maintenant nous sommes prêt à utiliser le nouveau parseur. Pour rendre disponible Sablotron dans la liste des DOMVendors, il suffit d'ajouter les 2 fichiers Sablot.aps et salbotxmldom.pas dans un paquet (par exemple user.dpk) de conception et de l'installer. On pourra aussi l'indiquer à l'exécution. Pour que l'enregistrement se fasse, il est impératif d'inclure notre unité dans la clause uses de l'unité utilisant le TXMLDocument.
la variable Sablot_DOM est l'instance du TDOMVendor enregistrée dans l'initialization de l'unité. Une fois que l'on a fixé le DOMVendor, on utilisera l'interface IXMLDocument pour manipuler le document XML. ConclusionLe composant TXMLDocument permet, en plus de manipuler facilement des documents XML, de changer de parseur permettant de gérer le XSL ou le XPath ce qui n'est actuellement pas possible avec le parseur OpenXML ou IBM livrés avec Kylix 2. Autre grand avantage de Sablotron, sa portabilité: On va pouvoir utiliser notre interfaçage aussi bien sous Linux que sous Windows. En plus de l'interfaçage en Pascal des bibliothèques des parseurs, il est nécessaire d'implémenter de façon spécifique, pour chaque parseur, les comportements pour les interfaces du DOM définies dans Kylix 2. Ce travail reste assez fastidieux compte tenu du nombre importants de méthodes. Voici l'interface en pascal ainsi que l'unité implémentation les interfaces du DOM pour utiliser le parseur Sablotron dans Kylix2. L'implémentation que j'en ai faite reste basique mais suffisante pour la plupart des utilisations. Sablotron: http://www.gingerall.com/charlie/ga/xml/p_sab.xml |