Search code examples
javascriptjsonxmlsaxon-js

Saxon-JS JSON to XML transformation only returns data from SEF file


I'm trying to create a Javascript tool that can convert XACML policies for AuthzForce to JSON and back using Saxon-JS. This documentation provides the required XSL stylesheets. Converting from XML to JSON works without a hitch but I'm having issues converting the JSON back to XML.

The transformation function returns a response with all the value data where the keys have been stripped from the SEF file in the "textContent" of the "principalResult".

This is the function I wrote to start the transformation and return an XML string:

function transformJsonToXml(input) {
  
  //Convert JSON object to string
  input = JSON.stringify(input);
  
  //Options for SaxonJS transformer
  var options = {
    stylesheetLocation: jsonToXmlStylesheet,
    sourceType: "json",
    inJsonFile: input,
    destination: "document",
    logLevel: "2"
  };

  //Transformation of JSON to XML
  let result = SaxonJS.transform(options);
  let policyXml = SaxonJS.serialize(result.principalResult);

  //Return the transformation result
  return policyXml;
}

This is the start of what I get back in "policyXml" which is just the data from the SEF but without any JSON formatting:

package301SaxonJS 2.6JS2TOP-LEVELtrue2024-04-03T14:59:27.482+01:00xml=~ xsl=~ xacml=urn:oasis:names:tc:xacml:3.0:core:schema:wd-17

This is the XSL file I have converted to SEF using the command xslt3 -t -xsl:stylesheet.xsl -export:stylesheet.sef.json -nogo -relocate:on -ns:##html5. The resulting SEF file can be found here.


Solution

  • The documentation of SaxonJS https://www.saxonica.com/saxon-js/documentation2/index.html#!api/transform shows the options sourceText which I think you would want to set to sourceText : input if you had the JSON as a string and that stylesheet expected the JSON as a string.

    I am not sure where you found the option inJsonFile for SaxonJS, however, it seems to be a global parameter of the stylesheet in https://github.com/authzforce/xacml-json-model/blob/develop/src/test/resources/xacml-policy-json-to-xml.xsl#L30 used with <xsl:apply-templates select="json-to-xml(unparsed-text($inJsonFile))" />, meaning the JSON is supposed to exist as a text/JSON file on disk to be loaded by the unparsed-text function.

    That stylesheet doesn't take a source input so I wouldn't set one, instead make sure get SaxonJS to read stylesheetParams : { inJsonFile: 'someFile.json' } in the options.

    I see also no use for -ns:##html5 when compiling that stylesheet.

    If you don't have a JSON file at all but only a JSON string, then you need to use a different stylesheet doing (make sure you have xmlns:xs="http://www.w3.org/2001/XMLSchema" declared in the XSLT stylesheet) e.g.

    <xsl:param name="jsonData" as="xs:string"/>
    
    <xsl:template name="xsl:initial-template">
      <xsl:apply-templates select="json-to-xml($jsonData)" />
    </xsl:template>
    

    and set e.g. stylesheetParams : { jsonData: input }.

    As an example:

    console.log(SaxonJS.XPath.evaluate(`transform(
      map {
        'stylesheet-location' : 'https://raw.githubusercontent.com/authzforce/xacml-json-model/develop/src/test/resources/xacml-policy-json-to-xml.xsl',
        'stylesheet-params' : map { QName('', 'inJsonFile') : 'https://raw.githubusercontent.com/authzforce/xacml-json-model/develop/src/test/resources/xacml%2Bjson.samples/Policies/valid/driver-access-policy.xacml.json' },
        'delivery-format' : 'serialized'
      }
    )?output`));
    <script src="https://martin-honnen.github.io/SaxonJS-2.6/SaxonJS2.js"></script>

    Or, if we have the JSON as a string and use a function:

    <script src="https://martin-honnen.github.io/SaxonJS-2.6/SaxonJS2.js"></script>
    <script type=module>
    var jsonInput = await (await fetch('https://raw.githubusercontent.com/authzforce/xacml-json-model/develop/src/test/resources/xacml%2Bjson.samples/Policies/valid/driver-access-policy.xacml.json')).text();
    
    console.log(
    SaxonJS.XPath.evaluate(`
      transform(
        map {
          'stylesheet-text' : '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"><xsl:import href="https://raw.githubusercontent.com/authzforce/xacml-json-model/develop/src/test/resources/xacml-policy-json-to-xml.xsl"/><xsl:param name="jsonData"/><xsl:template name="xsl:initial-template"><xsl:apply-templates select="json-to-xml($jsonData)"/></xsl:template></xsl:stylesheet>',
        'stylesheet-params' : map { QName('', 'jsonData') : $jsonInput },
        'delivery-format' : 'serialized'
      }
    )?output`, null, { params : { jsonInput: jsonInput}}));
    </script>