Reputation: 2200
From .NET code is it possible to directly invoke a specific XSLT template? i.e. ordinarily given code such as this: (VB.Net sorry, this is old code!)
Dim xsl As New System.Xml.Xsl.XslCompiledTransform()
xsl.Load(SomeXSLFile)
xsl.Transform(SomeXML, SomeArgs, SomeOutput)
The engine would process all templates in the XSL file according to whatever matches the XML document.
But say as part of my XSL file I had a named template:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:text>Some output</xsl:text>
<xsl:call-template name="Boilerplate"/>
</xsl:template>
<xsl:template name="Boilerplate">
<xsl:text>Generated by XSL</xsl:text>
</xsl:template>
</xsl:stylesheet>
Would there be a way to invoke this named template directly from the .NET code (either supplying a document or by assuming an empty document)
I can work around this by having rules in the XSL to detect specific XML patterns (e.g. <CallTemplate name="Boilerplate"/>
) and act accordingly but hoped there was a more natural way to do it?
Upvotes: 0
Views: 149
Reputation: 2200
Similar to Flynn1179's answer I have instead taken the existing XSL file and replace the match=/ template with one that just calls the named template instead.
This code is rough and should be tidied up before use. Also it assumes that only one XMLReader will ever be passed in, but does not check this.
Private Shared dicStaticTemplateContent As New Dictionary(Of String, String)
Private Shared Function GetStaticTemplateContent(xmrXSLFile As System.Xml.XmlReader, strTemplateName As String) As String
If Not dicStaticTemplateContent.ContainsKey(strTemplateName) Then
Dim xmd As New System.Xml.XmlDocument
xmd.Load(xmrXSLFile)
Dim nsm As New System.Xml.XmlNamespaceManager(xmd.NameTable)
nsm.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform")
Dim xmnRootTemplate As System.Xml.XmlNode = xmd.SelectSingleNode("/xsl:stylesheet/xsl:template[@match='/']", nsm)
Dim xmeNewRoot As System.Xml.XmlElement = xmd.CreateElement("template", "http://www.w3.org/1999/XSL/Transform")
If (xmnRootTemplate Is Nothing) Then
xmnRootTemplate = xmd.SelectSingleNode("/xsl:stylesheet/xsl:template[1]", nsm)
If (xmnRootTemplate Is Nothing) Then
xmd.DocumentElement.AppendChild(xmeNewRoot) 'quite pointless actually, if there are no templates then the thing won't work anyway, but this is a shortcut towards providing a somewhat more meaningful error anyway (from the xsl engine)
Else
xmd.DocumentElement.InsertBefore(xmeNewRoot, xmnRootTemplate)
End If
Else
xmnRootTemplate.ParentNode.ReplaceChild(xmeNewRoot, xmnRootTemplate)
End If
xmeNewRoot.InnerXml = "<xsl:call-template name=""" + strTemplateName + """/>"
xmeNewRoot.SetAttribute("match", "/")
Dim xsl As New System.Xml.Xsl.XslCompiledTransform()
Dim xss As New System.Xml.Xsl.XsltSettings(True, True)
xsl.Load(xmd, xss, Nothing)
Dim strResult = ProcessTransform(xmd, xsl, Nothing, Nothing)
dicStaticTemplateContent.Add(strTemplateName, strResult)
End If
Return dicStaticTemplateContent(strTemplateName)
End Function
Not all code paths have been tested (specifically where there is no match=/ template)
Also, beware of injection attacks on the template name
Upvotes: 0
Reputation: 12075
Realistically, there isn't a way to do this directly, but it might be possible to create a stylesheet which includes your target stylesheet using xsl:include or xsl:import, and then invokes the named template. Something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="(your stylesheet)"/>
<xsl:template match="/">
<xsl:call-template name="(template name)"/>
</xsl:template>
</xsl:stylesheet>
I VERY rarely use xsl:include or xsl:import though, I'm really not 100% sure how this would work, there might be an order of precendence issue with the template matching "\" between this stylesheet and yours, but hopefully this points you in a useful direction.
Upvotes: 2