node.jsxmlxslt-3.0saxon-js

how to convert json to xml with saxonjs?


I need to convert a json to xml with saxonjs, I don't know how to match keys to xml nodes, I'm looking for some examples for none of them work for me, this is my code

const issue = {
   id: 1,
   details: {
       type: 'urgent',
       description: 'Description of issue comes here',
       date: '2021-12-12',       
   }
};

saxonJS.transform({
        stylesheetLocation: './issue.sef.json',
        sourceType: 'json',
        sourceText: issue,
        destination: 'serialized',
    }, 'async').then(data => {
        fs.open('output.xml', 'w', function(err, fd) {
            fs.write(fd, data.principalResult, (err2, bytes) => {
                if(err2) {
                    console.log(err2);
                }
            });

        }); 

        res.status(200).send('Ok');
    })
    .catch(err => {
        console.log(err);
        res.status(500).send('error');
    });

And this is the output I'm trying to achieve

<xml>
     <issue id="1">
        <description>
            <![CDATA[
                Description of issue comes here
            ]]>
        </description>
        <type>urgent</type>
        <date>2021-12-12</date>
     </issue>
</xml>

Can you please help me with the xslt template?


Solution

  • Your shown input is a JavaScript object, it is not JSON in the strict syntax rules of the JSON spec.

    So I would think it is better to use JSON.stringify to create JSON and pass that to the XPath 3.1 function parse-json to create JSON or to make use of the Saxon-JS 2.3 feature to take JSON text, just make sure you have correctly JSON.stringifyed that object.

    As for a sample XSLT, that looks easy, for readability of the XSLT the below sample just uses a JavaScript string with the XSLT source code and runs it through the Saxon API:

    const SaxonJS = require("saxon-js");
    
    const issue = {
       id: 1,
       details: {
           type: 'urgent',
           description: 'Description of issue comes here',
           date: '2021-12-12',       
       }
    };
    
    const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="yes">
      <xsl:output indent="yes" cdata-section-elements="description"/>
      
      <xsl:template match=".">
       <xml>
        <issue id="{?id}">
          <description>{?details?description}</description>
          <type>{?details?type}</type>
          <date>{?details?date}</date>
        </issue>
       </xml>
      </xsl:template>
    </xsl:stylesheet>`;
          
    
    const result = SaxonJS.XPath.evaluate(`transform(map {
      'stylesheet-text' : $xslt,
      'initial-match-selection' : parse-json($json),
      'delivery-format' : 'serialized'
    })?output`,
    [],
    { params : 
        {
            json : JSON.stringify(issue),
            xslt : xslt
        }
    });
    

    Of course, in the end you can first compile the XSLT to SEF/JSON and then run it as you tried.

    To give you an example XSLT that uses two different templates and apply-templates, the following, instead of processing the nested object/map with inline code pushes processing it to a different template:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="yes" xmlns:map="http://www.w3.org/2005/xpath-functions/map" exclude-result-prefixes="#all">
      <xsl:output indent="yes" cdata-section-elements="description"/>
      
      <xsl:template match=".[. instance of map(*) and map:contains(., 'id')]">
       <xml>
        <issue id="{?id}">
          <xsl:apply-templates select="?details"/>
        </issue>
       </xml>
      </xsl:template>
      
      <xsl:template match=".[. instance of map(*) and map:contains(., 'description')]">      
        <description>{?description}</description>
        <type>{?type}</type>
        <date>{?date}</date>
      </xsl:template>
      
    </xsl:stylesheet>