Flow Group SASFlow Group
L'erp des Processus Relationnels
Systeme d'Information – Audit Industriel
> Accueil > Expertises & Savoirs > Articles techniques
<div class="infotip"><b>Espace client&#160;:</b> <a href="login.aspx">suivez ce lien</a> pour entrer dans l'espace qui vous est réservé.</div>

Articles technique


Application d'une transformation XSLT
sur un noeud quelconque d'un document XML

read the English version English version

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.

Limitations de l'implémentation en .Net par rapport à MSXML

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 à implémenter un XPathNavigatorReader.

L'inconvénient de ces deux méthodes est qu'elles dissocient l'élément du document initial. Il ne devient donc plus possible d'accéder ni à ses ancêtres, ni à ses frères.

Passage de paramètres à la feuille de style

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>

Modification dynamique de la feuille de style

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.

Pour information

Cette modification consiste à ajouter un paramètre et un template sur l'élément racine. Dans le cas où un tel template existait déjà, il faudra le modifier pour lui ajouter un mode particulier.

/// <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); }
Attention !

Les noms du paramètre et du mode par défault ont été choisi par soucis de clareté. Il faudrait vérifier qu'ils n'existent pas déjà. Un bon moyen de trouver des noms uniques pourraient se faire via des GUID.

Application de test

Vous pouvez télécharger l'application de test.

Le mode d'emploi est relativement simple :

  1. charger un fichier XML,
  2. charger une feuille de style,
  3. sélectionner un chemin XPath et un mode de départ
  4. cliquer sur "GO".
Capture d'écran de l'application de test

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.

Quelques liens utiles...


les informations fournies ici le sont en tant que telles, sans aucune garantie d'aucune sorte.
© 2022 Flow Group SAS - Flow Group est une marque déposée de GL Conseil SA.