I work with a proprietary tool that leverages XSLT through the Saxon 10 engine.
I'm looking to find a way to send a payload from my XSL to an API endpoint. The payload is too large for a GET (which I was able to do in XSL) so I need it to be a POST.
The EXPath http-client extension is not available to me, but Saxon does allow you to call either .NET or Java functions.
I'm not a .NET developer, but I have used a variation of this code snippet below through Windows Workflow Foundation and that one submits fine. I thought maybe I could convert it to Saxon friendly XSLT to accomplish the same thing.
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);
What I want to do is replicate that kind of code but inside of an XSLT stylesheet using Saxon's ability to work with .NET.
Using the Saxonica documentation as a reference, along with other XSLT examples we have internally, I started writing this:
<xsl:stylesheet
version="3.0"
xmlns:net="clitype=System.Net.Http.HttpClient"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="postTest">
<xsl:variable name="req" select="net:new()"/>
</xsl:template>
</xsl:stylesheet>
I didn't get much further as even this reports an error from the Saxon engine.
I also tried ChatGPT to see if it would give me some ideas. It gave me similar examples and they too all generate "Cannot find a X-argument function in ...". This was the code it gave me:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:net="clitype:System.Net.Http"
xmlns:encoding="clitype:System.Text.Encoding"
exclude-result-prefixes="net encoding">
<xsl:template match="/">
<xsl:variable name="url" select="'https://httpbin.org/post'" />
<xsl:variable name="client" select="net:HttpClient()" />
<!-- Prepare the payload -->
<xsl:variable name="payload" select="'nothing'" />
<xsl:variable name="content" select="net:StringContent($payload, encoding:UTF8(), 'application/json')" />
<!-- Send the POST request -->
<xsl:variable name="response" select="net:HttpClient.PostAsync($url, $content)" />
<!-- Read the response -->
<xsl:variable name="responseBody" select="net:HttpResponseMessage.Content.ReadAsStringAsync($response)" />
<!-- Output the response body -->
<xsl:message>Response:</xsl:message>
<xsl:value-of select="$responseBody"/>
</xsl:template>
</xsl:stylesheet>
Does anyone here have any experience doing something like this within XSLT or .NET? I don't even care if it's .NET as Java is also supported in Saxon.
I really need to be able to POST a portion of my XML data to an external API, but I cannot add any additional libraries to my XSLT processor or Saxon.
Any suggestions are appreciated.
I fumbled some reflexive Java calls together that at least run through both Saxon EE 10.9 Java as well as .NET that do a POST request:
<?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>