1

I am using nodejs and typescript. It is necessary to get such an xml file structure from the object, in which the number of nested Rub tags is arbitrary:

    <Doc file="jdsf35aasdg">
        <Rub id="1" name="Руб1" />
        <Rub id="2" name="Руб2" />
        <Rub id="3" name="Руб3" />
    </Doc>

The structure of the original object is not important to me, only the resulting xml. The closest result I got was with this initial object:

const doc = {
    Doc : {
        $ : {
            "file" : "jdsf35aasdg"
        },
        Rub : {
            $: {
                id : "1",
                name : "Руб1"
            }
        }
    }
};

const builder = new xml2js.Builder({
    headless: true
});

return builder.buildObject(object);


<Doc file="jdsf35aasdg">
     <Rub id="1" name="Руб1"/>
</Doc>

But I could not set an array instead of one element - an error of the object structure. How do I need to set an object in order to get the result shown above as a result of the conversion to xml?

Thanks in advance!

Dmitry
  • 93
  • 1
  • 6

1 Answers1

0

If you want to construct XML then XSLT is a good choice, in Node.js and in client-side JavaScript you have XSLT 3.0 and XPath 3.1 support thanks to Saxon-JS (https://www.npmjs.com/package/saxon-js), so your example used in Node.js would look like

const SaxonJS  = require("saxon-js");

var myObjects = [{ id : 1, name : 'Руб1' }, {id : 2, name : 'Руб2'}, { id : 3, name : 'Руб3'}];

var myFile = 'jdsf35aasdg';

const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">

  <xsl:param name="file" as="xs:string"/>
  <xsl:param name="rubs" as="map(*)*"/>
  
  <xsl:output indent="yes"/>
  
  <xsl:template name="xsl:initial-template">
    <Doc file="{$file}">
      <xsl:iterate select="$rubs">
        <Rub id="{?id}" name="{?name}"/>
      </xsl:iterate>
    </Doc>
  </xsl:template>
</xsl:stylesheet>`;

const result = SaxonJS.XPath.evaluate(`
  transform(
    map {
      'stylesheet-text' : $xslt,
      'delivery-format' : 'serialized',
      'stylesheet-params' : map {
        QName('', 'file') : $file,
        QName('', 'rubs') : $rubs
      }
    }
  )?output
`, [], {
  params : {
    xslt : xslt,
    file : myFile,
    rubs : myObjects
    }
    });
    
console.log(result);

and output e.g.

<?xml version="1.0" encoding="UTF-8"?>
<Doc file="jdsf35aasdg">
   <Rub id="1" name="Руб1"/>
   <Rub id="2" name="Руб2"/>
   <Rub id="3" name="Руб3"/>
</Doc>

You can also run it in the browser:

var myObjects = [{ id : 1, name : 'Руб1' }, {id : 2, name : 'Руб2'}, { id : 3, name : 'Руб3'}];

var myFile = 'jdsf35aasdg';

const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">

  <xsl:param name="file" as="xs:string"/>
  <xsl:param name="rubs" as="map(*)*"/>
  
  <xsl:output indent="yes"/>
  
  <xsl:template name="xsl:initial-template">
    <Doc file="{$file}">
      <xsl:iterate select="$rubs">
        <Rub id="{?id}" name="{?name}"/>
      </xsl:iterate>
    </Doc>
  </xsl:template>
</xsl:stylesheet>`;

const result = SaxonJS.XPath.evaluate(`
  transform(
    map {
      'stylesheet-text' : $xslt,
      'delivery-format' : 'serialized',
      'stylesheet-params' : map {
        QName('', 'file') : $file,
        QName('', 'rubs') : $rubs
      }
    }
  )?output
`, 
 [], 
 {
  params : {
    xslt : xslt,
    file : myFile,
    rubs : myObjects
  }
 }
);
    
console.log(result);
    
<script src="https://martin-honnen.github.io/Saxon-JS-2.4/SaxonJS2.js"></script>
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thank you! Perfect solution. It has only one drawback - high complexity. In addition to supporting the objects themselves, you also have to support the XSLT transformation for each. – Dmitry Oct 18 '22 at 07:17