Project

Profile

Help

Performing HTTP POST from XSLT using either Java or .NET calls from Saxon

Added by Matt Klem 8 days ago

My apologies for the cross-post. I think I had this in the wrong forum. (Mods can feel free to delete the one in the "Open Discussion")

I was directed to try and ask about this request via my original post on Stack Overflow. You can find that post here: https://stackoverflow.com/questions/79148922/looking-to-perform-http-post-from-xslt-using-either-java-or-net-calls-from-saxo/79155315

I am trying to see if it is possible to perform an HTTP POST operation from an XSLT sheet, using the .NET extensions within Saxon. I cannot add any additional libraries (including the EXPath HTTP-client ones) to my environment because Saxon is rolled into a custom 3rd party application.

I am currently using Saxon EE v10.6 through a proprietary third party tool. The company I work for does not own this tool, but we use it daily as part of various services engagement with customers. This tool uses Saxon to perform XSLT transformations, primarily taking XML and converting it into Office Open XML to generate native MS Office documents.

Historically, when we've hit limits from this 3rd party app, we've written custom .NET DLLs that contain our own code, and invoked them from an XSLT stylesheet using the "clitype" qualifier on a namespace. This has worked extremely well and allowed us to extend functionality as needed.

However, we're now in a situation where the 3rd party use of Saxon is now executed within a cloud environment space. As a result, we can no longer add custom DLLs that our XSLT code can refer to. We can only use Saxon and any of the already-included libraries that come with it. We've tested the ability to invoke .NET from the cloud version with things like xmlns:test="clitype:System.Math" and test:Sqrt(64), etc so we know the cloud version still lets us call .NET. We just need to figure out how to do it for an HTTP POST.

Since I know we can refer to .NET namespaces/classes through clitype, I'm trying to see if there's anyway to replicate this type of .NET code through a stylesheet:

var url = "https://httpbin.org/post";
var client = new HttpClient;
var content = new StringContent(payload, System.Text.Encoding.UTF8,"application/json");
var response = await client.PostAsync(url, content);

And yes, I know this is a barebones example, but I just want to show something close to what I want to do. I've used a variation of that .NET code in Windows Workflow Foundation to successfull pass a JSON payload to an API endpoint (Power Automate specifically).

On the StackOverflow post, someone gave me a sheet with the logic worked out using Java but my third party tool rejects it with a weird meaningless error.

I was hoping someone here might have any suggestions on how to craft the XSLT to use .NET methods for invoking an HTTP post.

Any thoughts or suggestions are greatly appreciated. Thanks.

Matt


Replies (22)

Please register to reply

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Michael Kay 8 days ago

Doing this without any custom "glue" code is certainly a bit challenging, especially because of the asynchrony.

I don't really know the .NET asynchronous API well enough to be sure how to achieve this, but in principle, if you can achieve the effect of "await" by applying a series of method calls to the Task<HttpResponseMessage> returned by the PostAsync method, then it ought to be possible to replicate those calls as XPath extension function calls.

What you can't do is use "await" explicitly.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 7 days ago

Mike,

what is the magic needed in Saxon EE .NET to get a reflexive constructor with clitype to work?

My attempts fail e.g. the stylesheet

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  xmlns:HttpClient="clitype:System.Net.Http.HttpClient"
  xmlns:StringContent="clitype:System.Net.Http.StringContent"
  xmlns:HttpResponseMessage="clitype:System.Net.Http.HttpResponseMessage"
  xmlns:HttpContent="clitype:System.Net.Http.HttpContent"
  xmlns:Task="clitype:System.Threading.Tasks.Task"
  xmlns:Encoding="clitype:System.Text.Encoding"
  exclude-result-prefixes="#all">

  <xsl:param name="staticHttpClient" select="HttpClient:new()"/>

  <xsl:param name="json-string" as="xs:string">{ "name" : "foo", "data" : [1, 2, 3] }</xsl:param>

  <xsl:function name="mf:HttpPost" as="xs:string" visibility="public" new-each-time="yes">
    <xsl:param name="uri" as="xs:string"/>
    <xsl:param name="content" as="xs:string"/>
    <xsl:param name="content-type" as="xs:string"/>
    <xsl:variable name="body" select="StringContent:new($content, Encoding:UTF8(), $content-type)"/>
    <xsl:variable name="response" select="HttpClient:PostAsync($staticHttpClient, $uri, $body)"/>
    <xsl:message select="Task:Wait($response)"/>
    <xsl:variable name="result" select="Task:Result($response)"/>
    <xsl:message select="HttpResponseMessage:EnsureSuccessStatusCode($result)"/>
    <xsl:variable name="resultContent" select="HttpResponseMessage:Content($result)"/>
    <xsl:variable name="stringTask" select="HttpContent:ReadAsStringAsync($resultContent)"/>
    <xsl:message select="Task:Wait($stringTask)"/>
    <xsl:variable name="resultString" select="Task:Result($stringTask)"/>
    <xsl:message select="Task:Dispose($response)"/>
    <xsl:message select="HttpResponseMessage:Dispose($result)"/>
    <xsl:message select="Task:Dispose($resultContent)"/>
    <xsl:message select="Task:Dispose($stringTask)"/>

    <xsl:sequence select="$resultString"/>
  </xsl:function>

  <xsl:output method="text"/>

  <xsl:template match="/" name="main">
    <xsl:value-of select="mf:HttpPost('https://httpbin.org/post', $json-string, 'application/json')"/>
    <xsl:text>&#10;Run with </xsl:text>
    <xsl:value-of select="system-property('xsl:product-name'), system-property('xsl:product-version'), 'at', current-dateTime()" separator=" "/>
  </xsl:template>

</xsl:stylesheet>

when run with Saxon EE 10.9 .NET Transform.exe -it:main -t -TJ does not even manage to instantiate the HttpClient:

Saxon-EE 10.9N from Saxonica
.NET 4.0.30319.42000 on Microsoft Windows NT 6.2.9200.0
Using license serial number ....
URIResolver.resolve href="file:/C:/Users/marti/OneDrive/Documents/xslt/blog-xslt-3-by-example/SaxonEE10Net/./http-post-request-test2.xsl" base="null"
Looking for function Q{clitype:System.Net.Http.HttpClient}new#0
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Trying net.sf.saxon.style.StylesheetFunctionLibrary
Trying net.sf.saxon.functions.FunctionLibraryList
Looking for function Q{clitype:System.Net.Http.HttpClient}new#0
Trying com.saxonica.ee.extfn.VendorFunctionSetEE
Trying net.sf.saxon.functions.MathFunctionSet
Trying net.sf.saxon.ma.map.MapFunctionSet
Trying net.sf.saxon.ma.arrays.ArrayFunctionSet
Trying net.sf.saxon.functions.registry.ExsltCommonFunctionSet
Trying com.saxonica.functions.extfn.EXPathFileFunctionSet
Trying net.sf.saxon.functions.registry.OnDemandFunctionSet
Function Q{clitype:System.Net.Http.HttpClient}new not found!
Trying net.sf.saxon.functions.registry.ConstructorFunctionLibrary
Trying net.sf.saxon.query.XQueryFunctionLibrary
Trying net.sf.saxon.functions.IntegratedFunctionLibrary
Trying com.saxonica.config.DotNetExtensionLibrary
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Trying com.saxonica.config.JavaExtensionLibrary
Trying net.sf.saxon.style.StylesheetFunctionLibrary
Function Q{clitype:System.Net.Http.HttpClient}new not found!
Looking for function Q{clitype:System.Net.Http.HttpClient}new#0
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Trying net.sf.saxon.style.StylesheetFunctionLibrary
Trying net.sf.saxon.functions.FunctionLibraryList
Looking for function Q{clitype:System.Net.Http.HttpClient}new#0
Trying com.saxonica.ee.extfn.VendorFunctionSetEE
Trying net.sf.saxon.functions.MathFunctionSet
Trying net.sf.saxon.ma.map.MapFunctionSet
Trying net.sf.saxon.ma.arrays.ArrayFunctionSet
Trying net.sf.saxon.functions.registry.ExsltCommonFunctionSet
Trying com.saxonica.functions.extfn.EXPathFileFunctionSet
Trying net.sf.saxon.functions.registry.OnDemandFunctionSet
Function Q{clitype:System.Net.Http.HttpClient}new not found!
Trying net.sf.saxon.functions.registry.ConstructorFunctionLibrary
Trying net.sf.saxon.query.XQueryFunctionLibrary
Trying net.sf.saxon.functions.IntegratedFunctionLibrary
Trying com.saxonica.config.DotNetExtensionLibrary
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Trying com.saxonica.config.JavaExtensionLibrary
Trying net.sf.saxon.style.StylesheetFunctionLibrary
Function Q{clitype:System.Net.Http.HttpClient}new not found!
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Failed to load type System.Net.Http.HttpClient: Der Typ "System.Net.Http.HttpClient" in der Assembly "saxon-ee-10.9, Version=10.9.0.0, Culture=neutral, PublicKeyToken=e1fdd002d5083fe6" konnte nicht geladen werden.
Error in {HttpClient:new()} at char 0 in xsl:param/@select on line 14 column 65 of http-post-request-test2.xsl:
  XPST0017  Cannot find a 0-argument function named
  Q{clitype:System.Net.Http.HttpClient}new().

For the class Encoding the profile shows it looks at some place for Looking for method UTF8 in .NET type System.Text.Encoding but it seems to fail to do a similar search of e.g. Looking for constructor in .NET type System.Net.Http.HttpClient.

There is not really another engine to compare but embedded similar .NET code in XmlPrime 5 runs fine e.g. the following code

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  exclude-result-prefixes="#all">

  <xsl:param name="json-string" as="xs:string">{ "name" : "foo", "data" : [1, 2, 3] }</xsl:param>

  <msxsl:script language="cs" implements-prefix="mf">
    <msxsl:assembly name="System.Net.Http"/>
    <msxsl:using namespace="System.Net.Http"/>
    <msxsl:using namespace="System.Text"/>
    
    static readonly HttpClient httpClient = new HttpClient();

        public static string HttpPost(string uri, string content, string contentType)
        {
            var body = new StringContent(content, Encoding.UTF8, contentType);

            var response = httpClient.PostAsync(uri, body);

            response.Wait();

            var result = response.Result;

            result.EnsureSuccessStatusCode();

            var resultContent = result.Content;

            var stringTask = resultContent.ReadAsStringAsync();

            stringTask.Wait();

            var resultString = stringTask.Result;

            response.Dispose();

            result.Dispose();

            resultContent.Dispose();

            stringTask.Dispose();

            return resultString;

        }
  </msxsl:script>


  <xsl:output method="text"/>

  <xsl:template match="/" name="main">
    <xsl:value-of select="mf:HttpPost('https://httpbin.org/post', $json-string, 'application/json')"/>
    <xsl:text>&#10;Run with </xsl:text>
    <xsl:value-of select="system-property('xsl:product-name'), system-property('xsl:product-version'), 'at', current-dateTime()" separator=" "/>
  </xsl:template>

</xsl:stylesheet>

there is processed and outputs e.g.

{
  "args": {},
  "data": "{ \"name\" : \"foo\", \"data\" : [1, 2, 3] }",
  "files": {},
  "form": {},
  "headers": {
    "Content-Length": "38",
    "Content-Type": "application/json; charset=utf-8",
    "Host": "httpbin.org",
    "X-Amzn-Trace-Id": "Root=1-67389bb8-680c6725188a768c42519a53"
  },
  "json": {
    "data": [
      1,
      2,
      3
    ],
    "name": "foo"
  },
  "origin": "176.199.255.78",
  "url": "https://httpbin.org/post"
}

Run with XmlPrime 5.0.6.25607 at 2024-11-16T14:18:48.7573939+01:00

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 6 days ago

It seems I have found a way to find the assembly, using e.g. xmlns:HttpClient="clitype:System.Net.Http.HttpClient?partialname=System.Net.Http" shows me traces like

Assembly System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a successfully loaded
Assembly codebase (GAC): file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Net.Http/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Net.Http.dll
Looking for method new in .NET type System.Net.Http.HttpClient
Number of actual arguments = 0
Looking for a constructor

so that part seems to be solved.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Michael Kay 6 days ago

Good you're making progress, please keep us informed.

I'm afraid I've always found dynamic loading in .NET to be a bit of a nightmare, which is why we've tried to reduce or eliminate the need for it in SaxonCS.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 6 days ago

As the HttpClient code I have used various generic classes/type parameters like Task<T> and I couldn't find a way to declare them properly with clitype I have tried to use the deprecated WebClient class instead, simplest code I have come up with is e.g.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  xmlns:WebClient="clitype:System.Net.WebClient?partialname=System.Net"
  exclude-result-prefixes="#all">

  <xsl:param name="staticWebClient" select="WebClient:new()"/>

  <xsl:param name="json-string" as="xs:string">{ "name" : "foo", "data" : [1, 2, 3] }</xsl:param>

  <xsl:param name="uri" as="xs:string" select="'https://httpbin.org/post'"/>

  <xsl:output method="text"/>

  <xsl:template match="/" name="main">
    <xsl:variable name="response" select="WebClient:UploadString($staticWebClient, $uri, $json-string)"/>
    <xsl:sequence select="$response"/>
    <xsl:text>&#10;Run with </xsl:text>
    <xsl:value-of select="system-property('xsl:product-name'), system-property('xsl:product-version'), 'at', current-dateTime()" separator=" "/>
  </xsl:template>

</xsl:stylesheet>

on running this through Saxon EE 10.9 .NET Transform.exe unfortunately it throws an error:

Saxon-EE 10.9N from Saxonica
.NET 4.0.30319.42000 on Microsoft Windows NT 6.2.9200.0
Using license serial number ...
URIResolver.resolve href="file:/C:/Users/marti/OneDrive/Documents/xslt/blog-xslt-3-by-example/SaxonEE10Net/./http-post-web-client-test1.xsl" base="null"
** Failed to load type net.sf.saxon.om.DocumentInfo
java.lang.UnsupportedOperationException
        at com.saxonica.expr.DotNetExtensionFunctionCall.copy(DotNetExtensionFunctionCall.java:188)
        at net.sf.saxon.expr.LetExpression.copy(LetExpression.java:710)
        at net.sf.saxon.style.XSLTemplate.compileTemplateRule(XSLTemplate.java:732)
        at net.sf.saxon.style.XSLTemplate.compileDeclaration(XSLTemplate.java:650)
        at net.sf.saxon.style.PrincipalStylesheetModule.compile(PrincipalStylesheetModule.java:1244)
        at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:322)
        at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:252)
        at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:113)
        at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:851)
        at net.sf.saxon.Transform.doTransform(Transform.java:753)
        at cli.Saxon.Cmd.DotNetTransform.Main(Unknown Source)
Fatal error during transformation: java.lang.UnsupportedOperationException:  (no message)

The above shows no errors in terms of failures to resolve/load the used WebClient and its constructor and its method but somehow fails otherwise.

I am not sure what to change.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 6 days ago

As an FYI, here is the other sheet I was given that uses Java instead of .NET (came from StackOverflow). This does work in Oxygen but spits up unusual errors in my tool.

<?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:BufferedReader="java:java.io.BufferedReader"
    xmlns:InputStreamReader="java:java.io.InputStreamReader"
    xmlns:OutputStream="java:java.io.OutputStream"
    xmlns:HttpURLConnection="java:java.net.HttpURLConnection"
    xmlns:URL="java:java.net.URL"
    xmlns:JString="java:java.lang.String"
    xmlns:Stream="java:java.util.stream.Stream"
    xmlns:Charset="java:java.nio.charset.Charset"
    xmlns:ByteBuffer="java:java.nio.ByteBuffer"
    xmlns:StandardCharsets="java:java.nio.charset.StandardCharsets"
    xmlns:jt="http://saxon.sf.net/java-type"
    exclude-result-prefixes="#all"
    version="3.0">
    
    <xsl:param name="payload" as="xs:string" expand-text="no">{ "Name" : "John Doe", "Age" : 42 }</xsl:param>
    
    <xsl:param name="url" as="xs:string">https://httpbin.org/post</xsl:param>
   
    <xsl:template name="xsl:initial-template" expand-text="yes">
        <xsl:variable name="uri" as="xs:anyURI" select="xs:anyURI($url)"/>
        <xsl:variable name="java-url" as="jt:java.net.URL" select="URL:new($uri)"/>
        <xsl:variable name="conn" select="URL:openConnection($java-url)"/>
        <xsl:message select="HttpURLConnection:setRequestMethod($conn, 'POST')"/>
        <xsl:message select="HttpURLConnection:setRequestProperty($conn, 'Content-Type', 'application/json; utf-8')"/>
        <xsl:message select="HttpURLConnection:setRequestProperty($conn, 'Accept', 'application/json')"/>
        <xsl:message select="HttpURLConnection:setDoOutput($conn, true())"/> 
        <xsl:variable name="os" select="HttpURLConnection:getOutputStream($conn)"/>
        <xsl:variable name="utf8Charset" select="Charset:forName('utf-8')"/>
        <xsl:variable name="payloadJavaString" as="jt:java.lang.String" select="JString:new($payload)"/>
        <xsl:variable name="byteArray" select="ByteBuffer:array(Charset:encode($utf8Charset, $payloadJavaString))"/>
        <xsl:message select="OutputStream:write($os, $byteArray, 0, count($byteArray))"/>
        <xsl:message select="OutputStream:close($os)"/>
        <xsl:variable name="responseCode" select="HttpURLConnection:getResponseCode($conn)"/>
        <xsl:variable name="br" select="BufferedReader:new(InputStreamReader:new(HttpURLConnection:getInputStream($conn), 'utf-8'))"/>
        <xsl:variable name="response" select="BufferedReader:lines($br) => Stream:toArray()"/>
        <result code="{$responseCode}">{$response}</result>
    </xsl:template>
    
</xsl:stylesheet>

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 5 days ago

Matt,

can you try the code from https://saxonica.plan.io/boards/3/topics/9759?r=9764#message-9764 in your environment and tell us whether it works (not likely when it doesn't run through the Saxon EE .NET command line) and if it fails what error messages exactly it gives.

As for the example using Java that runs through Saxon from the command line and for you in oXygen but fails in your cloud environment, can you post the exact error message(s) you get there (if any) to see whether there is some way to adapt the code.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 5 days ago

As the stack of https://saxonica.plan.io/boards/3/topics/9759?r=9764#message-9764 suggests the let or xsl:variable might cause the crash (there doesn't seem to be a copy implementation of the DotNetExtensionFunctionCall) I have rewritten the code to not use that variable but do e.g.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  xmlns:WebClient="clitype:System.Net.WebClient?partialname=System.Net"
  exclude-result-prefixes="#all">

  <xsl:param name="staticWebClient" select="WebClient:new()"/>

  <xsl:param name="json-string" as="xs:string">{ "name" : "foo", "data" : [1, 2, 3] }</xsl:param>

  <xsl:param name="uri" as="xs:string" select="'https://httpbin.org/post'"/>

  <xsl:output method="text"/>

  <xsl:template match="/" name="main">
    <xsl:value-of select="WebClient:UploadString($staticWebClient, $uri, $json-string)"/>
    <xsl:text>&#10;Run with </xsl:text>
    <xsl:value-of select="system-property('xsl:product-name'), system-property('xsl:product-version'), 'at', current-dateTime()" separator=" "/>
  </xsl:template>

</xsl:stylesheet>

but unfortunately that gives a different crash

...
Finding best fit method with arguments:
java.lang.NullPointerException
        at net.sf.saxon.expr.Expression.getRetainedStaticContext(Expression.java:461)
        at net.sf.saxon.trace.ExpressionPresenter.startElement(ExpressionPresenter.java:302)
        at net.sf.saxon.expr.GlobalVariableReference.export(GlobalVariableReference.java:146)
        at com.saxonica.config.DotNetExtensionLibrary.getBestFit(DotNetExtensionLibrary.java:515)
        at com.saxonica.config.DotNetExtensionLibrary.bind(DotNetExtensionLibrary.java:418)
        at net.sf.saxon.functions.FunctionLibraryList.bind(FunctionLibraryList.java:126)
        at net.sf.saxon.expr.parser.XPathParser.parseFunctionCall(XPathParser.java:3279)
        at net.sf.saxon.expr.parser.XPathParser.parseBasicStep(XPathParser.java:2226)
        at net.sf.saxon.expr.parser.XPathParser.parseStepExpression(XPathParser.java:2102)
        at net.sf.saxon.expr.parser.XPathParser.parseRelativePath(XPathParser.java:2021)
        at net.sf.saxon.expr.parser.XPathParser.parsePathExpression(XPathParser.java:1983)
        at net.sf.saxon.expr.parser.XPathParser.parseSimpleMappingExpression(XPathParser.java:1997)
        at net.sf.saxon.expr.parser.XPathParser.parseUnaryExpression(XPathParser.java:1850)
        at net.sf.saxon.expr.parser.XPathParser.parseExprSingle(XPathParser.java:752)
        at net.sf.saxon.expr.parser.XPathParser.parseExpression(XPathParser.java:657)
        at net.sf.saxon.expr.parser.XPathParser.parse(XPathParser.java:519)
        at net.sf.saxon.expr.parser.ExpressionTool.make(ExpressionTool.java:91)
        at net.sf.saxon.style.StyleElement.makeExpression(StyleElement.java:699)
        at net.sf.saxon.style.XSLValueOf.prepareAttributes(XSLValueOf.java:55)
        at net.sf.saxon.style.StyleElement.processAttributes(StyleElement.java:594)
        at net.sf.saxon.style.StyleElement.processAllAttributes(StyleElement.java:531)
        at net.sf.saxon.style.StyleElement.processAllAttributes(StyleElement.java:534)
        at net.sf.saxon.style.XSLTemplate.processAllAttributes(XSLTemplate.java:428)
        at net.sf.saxon.style.PrincipalStylesheetModule.processAllAttributes(PrincipalStylesheetModule.java:589)
        at net.sf.saxon.style.PrincipalStylesheetModule.preprocess(PrincipalStylesheetModule.java:366)
        at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:288)
        at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:252)
        at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:113)
        at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:851)
        at net.sf.saxon.Transform.doTransform(Transform.java:753)
        at cli.Saxon.Cmd.DotNetTransform.Main(Unknown Source)
Fatal error during transformation: java.lang.NullPointerException:  (no message)

That is HE/open-source code so it is online at https://saxonica.plan.io/projects/saxonmirrorhe/repository/he/revisions/he_mirror_saxon_10_9/entry/src/main/java/net/sf/saxon/expr/Expression.java#L461 but does a plain assert parent != null; that seems to cause the error.

No idea how to avoid that with the XSLT code.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 5 days ago

Even eliminating all variables and having the barebones XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:WebClient="clitype:System.Net.WebClient?partialname=System.Net"
  xmlns:NetString="clitype:System.String"
  exclude-result-prefixes="#all">

  <xsl:output method="text"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:value-of select="WebClient:UploadString(WebClient:new(), 'https://httpbin.org/post', '{ &quot;name&quot; : &quot;foo&quot;, &quot;data&quot; : [1, 2, 3] }')"/>
  </xsl:template>

</xsl:stylesheet>

without -TJ tells me

Error near {... "foo", "data" : [1, 2, 3] ...} at char 0 in xsl:value-of/@select on line 12 column 169 of http-post-webrequest-test4.xsl:
  XPST0017  Cannot find a 3-argument function named
  Q{clitype:System.Net.WebClient?partialname=System.Net}UploadString(). For diagnostics on
  calls to .NET methods, use the -TJ command line option or call
  processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")
Errors were reported during stylesheet compilation

and the -TJ then tells me

Finding best fit method with arguments:
Trying option 0: System.String UploadString(System.String, System.String)
Arguments cannot be converted to required types
Trying option 1: System.String UploadString(System.Uri, System.String)
Arguments cannot be converted to required types
Number of candidates remaining: 0
There are 2 candidate .NET members matching the function name, but none is a unique best match
More than one method, property, or field matching WebClient:UploadString with 3 parameters found in class WebClient, and there is insufficient type information to decide which to use

I am not sure how to tell Saxon to use the XDM string passed in as a .NET System.String.

Perhaps Mike knows some way.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 4 days ago

As asked, here's a bunch of errors and screenshots showing my issue. postTest.xsl is the file that contains the Java/.Net code, and DocumentMaster.dmast is an XSLT sheet that calls the postTest.

When doing the Java version, this is what my tool shows:

Here is the actual error: Error Attribute end value delimiter expected. Line 37, position 73. XML Editor postTest.xsl Error EmptyTemplate.xaml DocumentMaster.dmast java.lang.NullPointerException


On the WebClient side, here's what I get:

Actual errors reported are:

Error Errors were reported during stylesheet compilation EmptyTemplate.xaml DocumentMaster.dmast Errors were reported during stylesheet compilation

Error 8:0 - Cannot find a 3-argument function named Q{clitype:System.Net.WebClient?partialname=System.Net}UploadString(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true") Source: file:///C:/Users/MyUser/AppData/Local/Temp/Preview/Source/EmptyTemplate/EmptyTemplate_xaml/DocumentMaster_dmast/postTest.xsl EmptyTemplate.xaml postTest.xsl EDG.SaxonWrapper.SaxonWrapperException: 8:0 - Cannot find a 3-argument function named Q{clitype:System.Net.WebClient?partialname=System.Net}UploadString(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true") Source: file:///C:/Users/MyUser/AppData/Local/Temp/Preview/Source/EmptyTemplate/EmptyTemplate_xaml/DocumentMaster_dmast/postTest.xsl

Error 6:0 - Conflicting values for output property method Source: file:///C:/Users/MyUser/AppData/Local/Temp/Preview/Source/EmptyTemplate/EmptyTemplate_xaml/DocumentMaster_dmast/postTest.xsl EmptyTemplate.xaml postTest.xsl EDG.SaxonWrapper.SaxonWrapperException: 6:0 - Conflicting values for output property method Source: file:///C:/Users/MyUser/AppData/Local/Temp/Preview/Source/EmptyTemplate/EmptyTemplate_xaml/DocumentMaster_dmast/postTest.xsl


In Oxygen, using the Java version, I get a valid data returned:

I can't test the .NET one in Oxygen because, honestly, I have no idea how to configure it to allow me to use the .NET extensions.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 4 days ago

Just to make sure, can you show us your line 37 that is mentioned in the error message "Error Attribute end value delimiter expected. Line 37, position 73."?

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 4 days ago

Here's the screenshot. Weird thing is, Oxygen doesn't report this error at all.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Michael Kay 3 days ago

It doesn't surprise me that it's easier to get this working with SaxonJ than with Saxon on .NET. The reflexive API in SaxonJ has received much more attention over the years, and in any case, dynamic loading of classes seems to be much easier on the Java platform. Oxygen, as far as I'm aware, is always using the Java product.

Saxon 10 on .NET is built using IKVMC, so in principle you can call out either to Java methods (provided by the IKVMC runtime) or to .NET methods.

The error message "attribute end value delimiter expected" is not a Saxon error message. It feels to me like an error from the XML parser (though I can't find an exact match for the message on the web,).

The error message "Cannot find a 3-argument function named Q{clitype:System.Net.WebClient?partialname=System.Net}UploadString()" does come from Saxon. The -TJ output is trying to explain the reason:

Finding best fit method with arguments:
Trying option 0: System.String UploadString(System.String, System.String)
Arguments cannot be converted to required types
Trying option 1: System.String UploadString(System.Uri, System.String)
Arguments cannot be converted to required types
Number of candidates remaining: 0
There are 2 candidate .NET members matching the function name, but none is a unique best match
More than one method, property, or field matching WebClient:UploadString with 3 parameters found in class WebClient, and there is insufficient type information to decide which to use

We wouldn't get this far if it couldn't load the WebClient class. But why can't it convert an XPath string to a .NET string? I can't see any explanation for that. It's the simplest possible conversion, and the code in DotNetExtensionFunctionCall.atomicConversionPreference() certainly looks as if it allows it. I may have to resort to trying to reproduce it under a debuggers (which means resuscitating my Windows laptop and rediscovering the joys of Visual Studio, unfortunately. So it may take a little time.)

Further study of the code in the meantime. It's probably not the conversion of an xs:string literal to System.String that it's complaining about, it seems more likely to be the conversion of the first argument WebClient:new() to the target type WebClient. I'm trying to find the code that allows that conversion in DotNetConversionLibrary.getConversionPreferences() and I don't see where it's supposed to handle it. It feels it must be working because an awful lot of things would fail if it wasn't, but at present I don't see it.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 3 days ago

I have not been able to get the .NET clitype code approach to work reliably with Saxon .NET 10 EE.

As for the error "Attribute end value delimiter expected" you get with the Java code and Saxon .NET 10 EE in your environment, it seems a strange error message I have never seen from established XML parsers, a Google search for it, https://www.google.com/search?q=%22Attribute+end+value+delimiter+expected%22, only turns up our attempts solving your problem and two rather old threads of an activprosoftware editor control which seems gave such errors in 2012 but got a fix. So perhaps you can get your tool provider to make an update, perhaps then you will be able to use the Java code (that runs through both Saxon 10 EE Java and .NET from the command line without any errors or such error messages, they seem to come from your environment).

Or, as Mike has just posted he might look at the .NET/Windows code when he manages to resuscitate his Windows laptop, wait for that to shed some light on how to use the .NET code.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 3 days ago

My best guess on the delimiter error is just something to do with the way the proprietary tool is parsing that sheet. It wouldn't surprise me at all if they haven't implemented it correctly. We have seen it do other strange things like this.

So, for now, I guess my only next question would be, is if any of you have any other suggestions about how to execute an HTTP post from within my XSLT?

If I can't use .NET because of these issues, and the Java version kicks back an error, and I can't add the http-client extension, do you know of any other way I could perform a POST using just what comes bundled in Saxon 10 EE?

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 3 days ago

For trying to use .NET code with clitype, you will need to wait whether Michael can make some suggestions once he runs the stuff on a Windows system in a debugger.

The Java code works with Saxon EE, the error is with your environment, if the parsing error you have shown really prevents you from running/executing that code, I don't know how to change the Java code. As I said, I would suggest to try to contact the support of that proprietary tool to see whether they know a way to get to use the Saxon Java reflexive code, perhaps by updating their editor and/or parser component.

Other options only exist in the form of integrated extension functions but that requires you to write and compile and add .NET code, an approach that you said you could use in the past but no longer in the "cloud" environment.

There is also the option mentioned in https://www.saxonica.com/documentation10/index.html#!extensibility/dotnetextensions to

An easier approach is to link the code containing the extension functions into your C# or VB.NET code and pass Saxon a reference to the containing type. This can be done with:

Processor proc = new Processor(true);
proc.BindExtensions("http://example.com", typeof(Extension.Functions));

If you do this, then extension functions using the namespace URI "http://example.com" will be found in the class/type Extension.Functions without any need for dynamic loading.

but again that requires yo to be able to write/control the C# code to run the XSLT code, something I understand is not an option in your current environment.

I don't think there are other options built into Saxon 10 EE .NET.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Michael Kay 3 days ago

I think I've solved it without any actual debugging.

If there's one candidate method then it will work. If there's more than one, then it will decide that none of them match, because the test that the first argument matches is incorrect; and it will only apply this test if there is more than one candidate method.

I'll raise a bug issue on it, but I'm afraid that doing a new release of Saxon 10 on .NET isn't very high up our priority list. We were expecting .NET Framework to be out of support by now, but Microsoft seem to be extending it; we'll have to decide what to do. One possibility is making SaxonCS work on .NET Framework; but then we may also have to rethink the support for reflexive extension functions.

For most users the workaround would be to write your own method that calls the WebClient methods and that isn't itself overloaded. I appreciate that that's tricky for you given your project constraints. Unfortunately all the WebClient methods come in overloaded pairs taking either a String or a Uri argument which leaves no obvious way of getting around this.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Michael Kay 3 days ago

The other problems reported here include:

java.lang.UnsupportedOperationException
        at com.saxonica.expr.DotNetExtensionFunctionCall.copy(DotNetExtensionFunctionCall.java:188)

That is indeed an unimplemented method in the Saxon class for calling extension functions, and the main thing it reveals is how thin the test coverage is on this feature, both before we release, and in the field.

Finding best fit method with arguments:
java.lang.NullPointerException
        at net.sf.saxon.expr.Expression.getRetainedStaticContext(Expression.java:461)
        at net.sf.saxon.trace.ExpressionPresenter.startElement(ExpressionPresenter.java:302)
        at net.sf.saxon.expr.GlobalVariableReference.export(GlobalVariableReference.java:146)
        at com.saxonica.config.DotNetExtensionLibrary.getBestFit(DotNetExtensionLibrary.java:515)

This is a problem in the -TJ diagnostic code, though it could also occur on other paths, for example using -explain or -export on a stylesheet that contains reflexive extension function calls.

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 3 days ago

Thank you so much for the detail you have provided. It really is appreciated.

Yet today, the plot thickens...

The custom tool I have been writing and testing the code in is essentially a client-side app we write our XSL in. Once completed, we "publish" the code to a server that then runs the code server-side, when a request is made by a user.

I learned today, that although my "client" tool uses Saxon 10 EE, the server-side engine, when hosted in the cloud, runs Saxon 12 EE.

I don't have access to the cloud version yet, so I can't test either of the .NET or Java code I've put up here. I'm working on gaining that access.

But in the meantime, would any of you know if using Saxon 12 EE would resolve this issue? Or perhaps if Saxon 12 has some other built-in extension that would support my HTTP POST request.

Any ideas or suggestions for that are appreciated.

  • Matt

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Martin Honnen 3 days ago

You haven't even said whether it's the Java or the .NET version of Saxon EE 12. For the Java version you could try the reflexive Java approach, for SaxonCS EE 12 there is only https://www.saxonica.com/documentation12/index.html#!extensibility/extension-functions-CS

RE: Performing HTTP POST from XSLT using either Java or .NET calls from Saxon - Added by Matt Klem 3 days ago

You are absolutely correct. I totally forgot to mention which version I would be using. It would be Saxon 12 under. .Net

    (1-22/22)

    Please register to reply