3

I cannot find a way to capture the <xsl:message/> output of the .NET Saxon XSLT transformer when running via PowerShell.

I've tried various PowerShell methods of capturing output and various ways to tell Saxon to output the data without success. I need to use the Saxon XSLT library because our company is committed to it.

Tried

  1. Transformer.MessageListener. Problem: PowerShell doesn't support creating an interface to capture MessageListener.Message events

  2. Transformer.TraceFunctionDestination. Problem: Can't instantiate StandardLogger with a constructor.

    $outLogger = New-Object Saxon.Api.StandardLogger -ArgumentList $outLogger
    $outLogger.UnderylingTextWriter = $outLogFile
    $saxTransform.TraceFunctionDestination = $outLogger
    
  3. Various PowerShell output redirection methods. I don't think Saxon plays nice with that.

    $out = $saxTransform.Run($outSerializer) *> "c:\eric\log.txt"
    Write-Output "nada-> $out"
    

XSLT:

<xsl:template match="/">
  <xsl:message>I want to see this in a file</xsl:message>
</xsl:template>

PowerShell:

# Instantiate an Xslt transformer
$saxonPath = GetSaxonDllPath

Add-Type -Path $saxonPath
$saxProcessor = New-Object Saxon.Api.Processor
$saxCompiler = $saxProcessor.NewXsltCompiler()

$baseXsltPath = [System.IO.Path]::GetDirectoryName($inXsltPath)
$uri = [System.Uri]$baseXsltPath
$saxCompiler.BaseUri = $uri

Write-Output "Compiling..."
$uri = [System.Uri]$inXsltPath
$saxExecutable = $saxCompiler.Compile($uri)
$saxTransform = $saxExecutable.Load()

#Prepare the input XML file
$basePath = [System.IO.Path]::GetDirectoryName($inXmlFilePath)
$uri = [System.Uri]$basePath
$inFileStream = [System.IO.File]::OpenRead($inXmlFilePath)
$saxTransform.SetInputStream($inFileStream, $uri);

##Set XSLT processing parameters
if ($arguments) {
foreach($argument in $arguments.GetEnumerator()) {
  $paramName = New-Object Saxon.Api.QName($argument.Name)
  $paramValue = New-Object Saxon.Api.XdmAtomicValue($argument.Value)
  $saxTransform.SetParameter($paramName, $paramValue)
}
}

#Prepare the output file
$outFileStream = [System.IO.File]::OpenWrite($outFileName)
$outSerializer = New-Object Saxon.Api.Serializer
$outSerializer.SetOutputStream($outFileStream);

$outLogFile = [System.IO.File]::OpenWrite("c:\eric\log.txt")
$outLogger = New-Object Saxon.Api.StandardLogger($outLogFile)

#Transform
Write-Output "Transforming..."
$saxTransform.Run($outSerializer)

Write-Output "Transformation done."
$outLogFile.Close()
$outLogFile.Dispose()

#Cleanup
$inFileStream.Close()
$inFileStream.Dispose()

$outFileStream.Close()
$outFileStream.Dispose()
Tomalak
  • 332,285
  • 67
  • 532
  • 628
kratka
  • 122
  • 9
  • I think you *can* call a constructor with PS. Just give it an array as arguments. Try http://stackoverflow.com/q/12870109/18771 – Tomalak Mar 05 '15 at 18:30
  • Thanks for the reply. I actually believe PowerShell is properly providing the argument to the constructor of StandardLogger but for some reason that constructor may not be exposed to Powershell. Here's the response I get from PowerShell: New-Object : Cannot find an overload for "StandardLogger" and the argument count: "1". $outLogWriter = [System.IO.File]::OpenWrite("c:\eric\log.txt") $outLogger = New-Object Saxon.Api.StandardLogger -ArgumentList @($outLogWriter) $saxTransform.TraceFunctionDestination = $outLogger – kratka Mar 05 '15 at 20:19
  • The point of the answer I linked to is: You have to pass an *array* to `-ArgumentList`. Did you do that? – Tomalak Mar 05 '15 at 20:20
  • Yes, here's what I used #With argument list as array $outLogWriter = [System.IO.File]::OpenWrite("c:\eric\log.txt") $outLogger = New-Object Saxon.Api.StandardLogger -ArgumentList @($outLogWriter) $saxTransform.TraceFunctionDestination = $outLogger – kratka Mar 05 '15 at 20:22
  • Hm, in [the docs](http://www.saxonica.com/html/documentation/dotnetdoc/Saxon/Api/StandardLogger.html#StandardLogger()) it does not look like `StandardLogger()` even accepts any arguments. It has `UnderylingTextWriter {get; set; }` though, looks you mught be able to set that. – Tomalak Mar 05 '15 at 20:27
  • I also found this: https://saxonica.plan.io/boards/3/topics/6029 - maybe it isn't possible in .NET after all. – Tomalak Mar 05 '15 at 20:29
  • Thanks for your efforts. I can see 2 constructors in .NET where the 2nd takes in a TextWriter. Anyways I went toward using UnderlyingTextWriter and the code works but the TraceFunctionDestination doesn't emit the data. In case others might benefit from the code, here it is: $outLogWriter = New-Object System.IO.StreamWriter "c:\eric\log.txt" $outLogger = New-Object Saxon.Api.StandardLogger $dotNetWriter = New-Object net.sf.saxon.dotnet.DotNetWriter $outLogWriter $outLogger.UnderylingTextWriter = $dotNetWriter $saxTransform.TraceFunctionDestination = $outLogger – kratka Mar 05 '15 at 20:48
  • 1
    As a general tip: Don't post large amounts of code into the comments, that's always difficult to follow. Better edit into your question or - in case you found an answer yourself - post it as an actual answer. – Tomalak Mar 05 '15 at 20:52

1 Answers1

0

We have some sympathy with your requirement. But the use of the XslTransformer.MessageListener is the only why of intercepting where the xsl:message output is sent. Setting the TraceFunctionDestination will not capture the xsl:message output.

I am not an expert in Powershell, but as a workaround I would think you can write an implementation of IMessageListener in a C# class and build this as an assembly for use within Powershell.

ond1
  • 691
  • 6
  • 10