Reputation: 2543
I am using XSLT transformation template which works with MSSQL database in which i have to do some complex computing. I show piece of code below. Important is method GetZaloha. It returns large xml element which I want past it to output xml, but obv. this approach doesn't work. I tried to return value as string through xElem.ToString() but result is represented (ofc.) as string. so "<" and ">" marks are represented as escaped > and <. Do somebody have idea how provide some object to transformation which will be represented as xml structured text? Thank you very much.
<msxsl:script implements-prefix="utility" language="C#">
<msxsl:assembly name="System"/>
<msxsl:assembly name="System.Data"/>
<msxsl:using namespace="System"/>
<msxsl:using namespace="System.Xml"/>
<msxsl:assembly name="System.Core" />
<msxsl:assembly name="System.Xml.Linq" />
<msxsl:using namespace="System.Collections.Generic" />
<msxsl:using namespace="System.Linq" />
<msxsl:using namespace="System.Xml.Linq" />
<![CDATA[
public static XElement GetZaloha(string VariableSymbol)
{
XElement xElem = .....
return xElem;
}
]]>
</msxsl:script>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CisloDokladu">
<xsl:element name="CisloDokladu">
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:element>
<xsl:variable name="VariabilniSymbol" select="./VariabilniSymbol"/>
<xsl:element name="OdpoctyZaloh">
<xsl:attribute name="ObjectType">List</xsl:attribute>
<xsl:attribute name="ObjectName">OdpocetZalohy</xsl:attribute>
<xsl:value-of select="utility:GetZaloha($VariabilniSymbol)" />
</xsl:element>
</xsl:template>
Upvotes: 0
Views: 958
Reputation: 11
Five years after, but since I have just figured out the solution I post it anyway. The xsl engine only accepts a limited set of types. XPathNavigator is one and it allows to return a full xml sub-tree from the script into the document.
Here is some more info about the accepted return types https://learn.microsoft.com/en-us/dotnet/standard/data/xml/script-blocks-using-msxsl-script
The xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my-scripts="urn:my-scripts"
exclude-result-prefixes="msxsl my-scripts"
>
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<msxsl:script language="C#" implements-prefix="my-scripts">
<![CDATA[
public static XPathNavigator createXmlNodes(string text)
{
//prepare DOM with doc elent named "root"
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root/>");
var root = doc.DocumentElement;
//Add an element
var elem = doc.CreateElement("token");
elem.AppendChild(doc.CreateTextNode(text));
root.AppendChild(elem);
//Add an element
elem = doc.CreateElement("token2");
elem.AppendChild(doc.CreateTextNode(text));
root.AppendChild(elem);
//returnxpathNavigator, this will include "root"
return root.CreateNavigator();
// A second option using a string instead of a DOM
//var sr = new System.IO.StringReader("<token>" + text + "</token>");
//var xp = new XPathDocument(sr);
//return xp.CreateNavigator();
}
]]>
</msxsl:script>
<!-- Ignore Input just call function using fixed text -->
<xsl:template match="/">
<doc>
<!-- the /* is there to skip the root element -->
<xsl:copy-of select="my-scripts:createXmlNodes('Hello World')/*" />
</doc>
</xsl:template>
</xsl:stylesheet >
Empty dummy input
<?xml version="1.0" encoding="utf-8"?>
<empty/>
result
<?xml version="1.0" encoding="utf-8"?>
<doc>
<token>Hello World</token>
<token2>Hello World</token2>
</doc>
Upvotes: 1
Reputation: 635
Some solution: write output XML with XSLT transform extension object and use XmlWriter method WriteCData:
public class CDataWriter { public static XDocument Transform(XDocument doc, string fileXslt) { XsltArgumentList xslArg = new XsltArgumentList(); XslCompiledTransform trans = new XslCompiledTransform(); trans.Load(fileXslt); XDocument xmlOutput = new XDocument(); using (var writer = xmlOutput.CreateWriter()) { CDataWriter info = new CDataWriter(writer); xslArg.AddExtensionObject("urn:cdata", info); trans.Transform(input: doc.CreateReader(), arguments: xslArg, results: writer); } return xmlOutput; } protected CDataWriter(XmlWriter writer) { this.writer = writer; } XmlWriter writer; public string CData(object obj) { if (obj != null && obj is System.Xml.XPath.XPathNodeIterator) { var iter = obj as System.Xml.XPath.XPathNodeIterator; if (iter.Count > 0 && iter.MoveNext()) { var current = iter.Current; writer.WriteCData(current.OuterXml); } } return string.Empty; } }
Sample xslt file:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:trans="urn:cdata">
<xsl:template name="cdata">
<Result>
<xsl:value-of select="trans:CData(*)" />
</Result>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 4238
You are currently using <xsl:value-of>
to include your function result into your XML output. This tag always flattens the XML node tree by (more or less) extracting just the text parts from it. I would suggest that you use
<xsl:copy-of select="utility:GetZaloha($VariabilniSymbol)" />
instead which copies the complete node tree (without alteration) into the XML output.
Upvotes: 1