Articles technique
Application d'une transformation XSLT
sur un noeud quelconque d'un document XML
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.
/// <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);
}
Application de test
Vous pouvez télécharger l'application de test.
Le mode d'emploi est relativement simple :
- charger un fichier XML,
- charger une feuille de style,
- sélectionner un chemin XPath et un mode de départ
- cliquer sur "GO".
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...
Transforming only a part of XML Extrait du blog d'Oleg Tkachenko | |
Télécharger le source des tests taille 24 Ko, dernière modification 06/08/2013 | |
Nous contacter Vous avez des remarques, des questions ? N'hésitez pas à nous contacter. |