|
Fiches TechniqueApplication d'une transformation XSLT
|
Limitations de l'implémentation en .Net par rapport à MSXML |
Passage de paramètres à la feuille de style |
Modification dynamique de la feuille de style |
Application de test |
Quelques liens utiles... |
Un des principaux attraits du XML est sa capacité à décrire une structure hiérarchique, chaque élément pouvant être considéré comme un document XML à part entière. Ainsi, dans le cas d'un document XML complexe que l'on souhaite afficher sur un site web, on peut devoir transformer certains noeuds pour générer des pages HTML.
Avec MSXML, la tâche est facile. En effet, les méthodes transformNode et transformNodeToObject permettent d'effectuer une transformation sur le noeud sélectionné.
En revanche, en .Net, la transformation s'applique forcément sur le document entier, même si le paramètre passé est un élément. Pour contourner cette limitation, la documentation propose une méthode qui consiste à créer un nouveau document à partir de l'élément que l'on souhaite transformer. Dans son Blog, Oleg Tkachenko propose une autre méthode, plus élégante, qui consiste à implementer un XPathNavigatorReader.
L'inconvénient de ces deux méthodes est qu'elles dissocient l'élement du document initial. Il ne devient donc plus possible d'accéder ni à ses ancètres, ni à ses frères.
Heureusement, lors de l'exécution de la transformation, il est possible passer des paramètres au processeur. Alors pourquoi ne pas lui passer le noeud courant ?
Prenons l'exemple de la feuille de style ci-dessous :
<?xml version="1.0" encoding="utf-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <-- context parameter --> <xsl:param name="ContextStartingNode" /> <-- go directly to the context --> <xsl:template match="/"> <xsl:apply-templates select="$ContextStartingNode" > </xsl:template> <-- templates --> ... </xsl:stylesheet>
Pour passer le contexte au processeur, il suffit d'écrire le code suivant :
System.Xml.XPath.IXPathNavigable input; System.Xml.XmlDocument stylesheet; System.IO.TextWriter output; ... XslTransform transform = new XslTransform(); XsltArgumentList args = new XsltArgumentList(); // select the context node XPathNavigator navigator = input.CreateNavigator(); XPathNodeIterator it = navigator.Select(xpath); args.AddParam("ContextStartingNode", "", it); // create the transform object XmlResolver resolver = new XmlUrlResolver(); resolver.Credentials = System.Net.CredentialCache.DefaultCredentials; System.Security.Policy.Evidence evidence = this.GetType().Assembly.Evidence; transform.Load(stylesheet, resolver, evidence); // perform the transformation transform.Transform(input, args, output, resolver);
On remarque qu'avec cette approche, il est possible de changer de mode dans le template sur l'élément racine :
<xsl:template match="/"> <xsl:apply-templates select="$ContextStartingNode" mode="mode" > </xsl:template>
Maintenant, pour que cette approche soit applicable de manière générique, il ne reste plus qu'à modifier dynamiquement une feuille de style quelconque de manière transparente.
/// <summary> /// Transforms the input Xml document, using the nodes matching the XPath /// as context node, and starting with the specified mode /// </summary> /// <param name="input">input document</param> /// <param name="stylesheet">stylesheet to be used</param> /// <param name="output">output stream</param> /// <param name="xpath">xpath for the context node</param> /// <param name="startingMode">starting mode</param> public void Transform(System.Xml.XPath.IXPathNavigable input, System.Xml.XmlDocument stylesheet, System.IO.TextWriter output, string xpath, string startingMode) { // try to load the transform XslTransform transform = new XslTransform(); XsltArgumentList args = new XsltArgumentList(); // modify the stylesheet if((xpath != "/") || (startingMode != null && startingMode.Length > 0)) { string prefix = stylesheet.GetPrefixOfNamespace( @"http://www.w3.org/1999/XSL/Transform"); XmlElement param = stylesheet.CreateElement(prefix, "param", @"http://www.w3.org/1999/XSL/Transform"); param.SetAttribute("name", "ContextStartingNode"); stylesheet.DocumentElement.PrependChild(param); XmlElement template = stylesheet.CreateElement(prefix, "template", @"http://www.w3.org/1999/XSL/Transform"); template.SetAttribute("match", "/"); XmlElement rule = stylesheet.CreateElement(prefix, "apply-templates", @"http://www.w3.org/1999/XSL/Transform"); if(xpath != "/") { rule.SetAttribute("select", "$ContextStartingNode"); XPathNavigator navigator = input.CreateNavigator(); XPathNodeIterator it = navigator.Select(xpath); args.AddParam("ContextStartingNode", "", it); } else { rule.SetAttribute("select", "/"); } if(startingMode != null && startingMode.Length > 0) { rule.SetAttribute("mode", startingMode); } template.AppendChild(rule); stylesheet.DocumentElement.AppendChild(template); XmlNamespaceManager nsmgr = new XmlNamespaceManager(stylesheet.NameTable); nsmgr.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform"); XmlElement @default = stylesheet.SelectSingleNode( @"/*/xsl:template[@match='\' and not(@mode)]", nsmgr) as XmlElement; if(@default != null) { @default.SetAttribute("mode", "DefaultStartingMode"); } } // create the transform object XmlResolver resolver = new XmlUrlResolver(); resolver.Credentials = System.Net.CredentialCache.DefaultCredentials; System.Security.Policy.Evidence evidence = this.GetType().Assembly.Evidence; transform.Load(stylesheet, resolver, evidence); // perform the transformation transform.Transform(input, args, output, resolver); }
Vous pouvez télécharger l'application de test.
Le mode d'emploi est relativement simple :
Comme vous pouvez le constater sur la capture d'écran ci-dessus, le document XML a été recopié à partir du noeud sélectionné, en ajoutant un attribut contenant le chemin jusqu'au noeud.
Transforming only a part of XML Extrait du blog d'Oleg Tkachenko
|
|
Télécharger le source des tests taille : 25 Ko, dernière modification : 22/05/2004
|
|
Nous contacter Vous avez des remarques, des questions ? N'hésitez pas
à nous contacter.
|