4

I'm using Win7 and set my VSC# project to .NETFramework4. Then download SaxonHE9-8-0-7N-setup.exe and install. Then reference saxon9he-api.dll to C# project and using Saxon.Api; And here's my program.cs:

static void Main(string[] args)
{
    var xslt = new FileInfo(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory.ToString(), @"..\..\..")) + @"\TEST.xslt");
    var input = new FileInfo(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory.ToString(), @"..\..\..")) + @"\TEST.xml");
    var output = new FileInfo(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory.ToString(), @"..\..\..")) + @"\result.txt");

    var processor = new Processor();
    var compiler = processor.NewXsltCompiler();
    var executable = compiler.Compile(new Uri(xslt.FullName));
    var transformer = executable.Load();
    var serializer = new Serializer();

    FileStream outStream = new FileStream(output.ToString(), FileMode.Create, FileAccess.Write);
    serializer.SetOutputStream(outStream);

    using (var inputStream = input.OpenRead())
    {
        transformer.SetInputStream(inputStream, new Uri(Path.GetTempPath()));
        transformer.SetParameter(new QName("password"), new XdmAtomicValue("secret"));
        transformer.Run(serializer);
        outStream.Close();
    }
}

here's my TEST.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array"
    version="3.0">

  <xsl:output method="json" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="root">
    <xsl:map>
      <xsl:map-entry key="local-name()">
        <xsl:apply-templates/>
      </xsl:map-entry>
    </xsl:map>
  </xsl:template>

  <xsl:template match="items">
    <xsl:variable name="items" as="item()*">
      <xsl:apply-templates/>
    </xsl:variable>
    <xsl:sequence select="map { local-name() : array { $items }}"/>
  </xsl:template>

  <xsl:template match="item">
    <xsl:sequence select="map { 'foo' : xs:integer(foo), 'bar' : string(bar) }"/>
  </xsl:template>

</xsl:stylesheet>

Before runing I receive two error message:

The element 'template' in namespace 'http://www.w3.org/1999/XSL/Transform' has invalid child element 'map'

and

The 'as' attribute is not declared.

When running I receive one error message:

Error in xsl:map-entry/@key TEST.xslt:FOTY0013: Cannot write a function item to an XML tree in built-in template rule for /root in the unnamed mode**

So what should I do to run this code without error?

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
DaveG
  • 491
  • 1
  • 6
  • 19

3 Answers3

6

Change the line to create a Transformer to create an Xslt30Transformer

        var transformer = executable.Load30();

to use XSLT 3 and its various, different and more flexible input and output options and then to run the stylesheet use

        using (var inputStream = input.OpenRead())
        {
            transformer.ApplyTemplates(inputStream, serializer);
            outStream.Close();
        }

That way the XSLT code I posted earlier and you used as an example runs fine (I had to adapt the file paths but that obviously depends on how/where the files are in relation to the C# project).

Note that in general with XSLT 3 the initial match selection and the global context item to initialize global variables can be different, the sample stylesheet does not have any global variables or parameters, but if it had them you would also need to set the GlobalContextItem (https://www.saxonica.com/html/documentation/dotnetdoc/Saxon/Api/Xslt30Transformer.html#GlobalContextItem) to an XdmValue.

As for the various editor warnings or error messages you get when editing XSLT 3 in Visual Studio, well, simply installing an XSLT 3 processor on your system does not convert VS into an XSLT 3 editor, you will need to check whether/how you can set up Visual Studio to use the XSLT 3 schema, the XSLT 3 spec has one linked at https://www.w3.org/TR/xslt-30/schema-for-xslt30.xsd but I think it uses the schema language 1.1 while Microsoft only supports the schema language version 1.0 so it might be a bit difficult to find and install a schema for VS that makes it recognize and support XSLT 3 editing.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Saved my day!! But i'm surprised not much people have the same issue. – DaveG Jan 02 '18 at 09:09
  • 2
    I think most people doing serious XSLT development are using an XML-based IDE such as oXygen XML, Stylus Studio, or XML Spy, and unlike Visual Studio these all support XSLT 3.0 editing. – Michael Kay Jan 02 '18 at 14:41
  • 2
    To expand on the answer. The two compile-time error messages are because VS does not understand XSLT 3.0 - the messages come from VS, not from Saxon. The run-time error message is because Saxon's XsltTransformer API cannot handle the "json" output method; that's why you need to switch to the Xslt30Transformer API. – Michael Kay Jan 02 '18 at 14:44
  • 1
    @MichaelKay Visual Studio supports XSLT 3.0 editing, if you provide an XSD 1.0 schema of XSLT. – Max Toro Jan 07 '18 at 21:51
0

Because I had problem with translating this solution to HE-9.9, here is my solution - with caveat that it works for 9.9 and not for 9.8:

using System;
using System.IO;
using Saxon.Api;

namespace Project1
{
    public static class ClassMain
    {
        public static string TransformXml(string xmlData, string xslData)
        {
            var xsltProcessor = new Processor();
            var documentBuilder = xsltProcessor.NewDocumentBuilder();
            documentBuilder.BaseUri = new Uri("file://");
            var xdmNode = documentBuilder.Build(new StringReader(xmlData));

            var xsltCompiler = xsltProcessor.NewXsltCompiler();
            var xsltExecutable = xsltCompiler.Compile(new StringReader(xslData));
            var xsltTransformer = xsltExecutable.Load();
            xsltTransformer.InitialContextNode = xdmNode;

            var results = new XdmDestination();

            xsltTransformer.Run(results);
            return results.XdmNode.OuterXml;
        }

        public static void Main()
        {
            var xmlData = File.ReadAllText("a.xml");
            var xslData = File.ReadAllText("a.xsl");

            var data = TransformXml(xmlData, xslData);
            Console.WriteLine(data);
            Console.ReadKey();
        }
    }
}
Ch3shire
  • 1,095
  • 2
  • 14
  • 39
0

for 9.9 use var serializer = processor.NewSerializer();

Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
rdonca
  • 1
  • 2
    Why do you recommend this approach over the other recommendations? Are there benefits to this that are worth calling out? This is a good opportunity to help people understand not just _what_ to do but also _why_ to do it. – Jeremy Caney May 14 '20 at 01:15