Jaroslav Kadlec
Jaroslav Kadlec

Reputation: 2543

Transformation through msxsl namespace

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 &gt and &lt. 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

Answers (3)

Disco12
Disco12

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

Andrew
Andrew

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

Marcus Rickert
Marcus Rickert

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

Related Questions