How to Generate and Download XML on the fly without persisting in CAP Application(Node.js)

Estimated read time 9 min read

Use Case:

When the user clicks the “XML” button, the application will fetch the account details and payment information from the database. It will then prepare an XML file. This XML file will be downloaded and stored in the “downloads” folder on the user’s device.

The screenshot below shows the CAP application UI, which includes a custom button to trigger XML generation and download.

The downloaded XML looks like the screenshot below.

Implementation Steps:

Define a Virtual field in serviceHandler logic to generate and download xmlImporting necessary modulesImplementing the Read HandlerPreparing the JSON ContentConverting the JSON to XMLConverting XML data to buffer to downloadConverting “Open file” link to a button in the UI

Define a Virtual field in service:

Define a virtual field at the service level with type “LargeBinary” and media type “application/xml”.

entity AccountStatements {
virtual null    as   xml : LargeBinary @Core.MediaType: ‘application/xml’,
}

This will automatically generate a hyperlink labelled “Open File” in the UI as shown in the screenshot below.

Handler logic to generate and download xml:

To download an account statement in XML format, first, retrieve the XML data for the specified account statement ID using a database transaction. Then, convert the XML data to a buffer. Finally, set the file name based on the account statement ID and specify the content type as XML.

Importing Necessary Modules

To handle the conversion and streaming of the XML content, you’ll require the Buffer and Readable modules from Node.js. Import these modules into your project with the following code:

import { Buffer } from “buffer”;
import { Readable } from “stream”;

Add the “xml2js” library through package.json

Import the library to proceed the xml conversion

import xml2js from “xml2js”;

Implementing the Read Handler:

In the Read handler of the entity, verify if the URL path includes “xml”. (name of the virtual field defined in your entity). If you have named the virtual field differently, ensure to check for that specific name in the URL.

virtual null                as xml                  : LargeBinary @Core.MediaType: ‘application/xml’,

If the URL path contains the specified virtual field name(url.includes(“xml”)), proceed to generate the XML content. Convert this XML content into buffer data, push the buffered data into a Readable stream, and return it. Make sure to include the media content type and the disposition filename in the response.

Example:

/dch/ui/payment/accountstatements/AccountStatements(${object.ID})/xml

Preparing the JSON content :

Construct the json content with the retrieved data from the database.

private buildAccountStatementJson(
    accStmtEntries: Camt053Ntry[],
    AccountStatement: RetrieveAccountStatementDetails
  ) {
    const jsoncontent = {
      $: {
        xmlns: “urn:iso:std:iso:20022:tech:xsd:camt.053.001.12”,
        “xmlns:xsi”: “http://www.w3.org/2001/XMLSchema-instance“,
        “xsi:schemaLocation”: “urn:iso:std:iso:20022:tech:xsd:camt.053.001.12 camt.053.001.12.xsd”
      },
      BkToCstmrStmt: {
        GrpHdr: {
          MsgId: String(AccountStatement.accountStatementDisplayId),
          CreDtTm: AccountStatement.accountStatementCreatedAt
        },
        Stmt: {
          Id: String(AccountStatement.accountStatementDisplayId),
          LglSeqNb: String(AccountStatement.accountStatementDisplayId),
          CreDtTm: AccountStatement.accountStatementCreatedAt,
          Acct: {
            Id: {
              Othr: {
                Id: AccountStatement.accountDisplayId
              }
            }
          }
        }
      }
    };
    return jsoncontent;
  }

Converting the JSON to XML:

Convert the JSON content to XML format by passing the JSON data to the xml2js library function, enabling the download of the data as an XML file.

private jsonToCamt053Xml(jsondata: Camt053Document) {
    const builder = new xml2js.Builder({
      headless: true,
      renderOpts: {
        pretty: false // Set to true for pretty formatting
      }
    });
    let camt053Xml = {
      Document: jsondata
    };
    camt053Xml = builder.buildObject(camt053Xml);
    return camt053Xml;
  }

Converting XML data to buffer to download:

Adding the XML data to a buffer is necessary because buffers handle raw binary data, which is useful for file operations such as downloading. Converting the XML string to a buffer ensures that the data is correctly encoded and can be managed efficiently for creating the downloadable file. This step is crucial for maintaining data integrity during the download process.

        BufferData = Buffer.from(xmlData.xmlString, “utf8”);
        fileName = `AccountStatement${xmlData.accountStatementDisplayId}.xml`;
        contentType = “application/xml”;

Converting “Open File” link to a button in the UI:

 Define a UI Fragment that contains a button with the handler in app/webapp/ext/fragment section.

<core:FragmentDefinition xmlns:core=”sap.ui.core” xmlns=”sap.m”>
    <Button
        core:require=”{ handler: ‘sap/erp4sme/c4b/payment/accountstatements/ext/fragment/DownloadXML’}”
        text=”XML”
        press=”handler.onPress” />
</core:FragmentDefinition>

Create a handler for the button that, when pressed, displays a toast message saying “DownloadingXML” and then redirects the browser to the XML URL.

sap.ui.define([
    “sap/m/MessageToast”
], function (MessageToast) {
    ‘use strict’;
    return {
        onPress: function (oEvent) {
            const view = this.editFlow.getView();
            const i18nModelBundle = view.getModel(“i18n”).getResourceBundle();
            MessageToast.show(i18nModelBundle.getText(“DownloadingXml”));
            const object = oEvent.getSource().getBindingContext().getObject();
            sap.m.URLHelper.redirect(`/dch/ui/payment/accountstatements/AccountStatements(${object.ID})/xml`, true);
        }
    };
});

Add the fragment in the columns section in manifest.json.

                   “DownloadXML”: {
                      “position”: {
                        “anchor”: “DataField::createdAt”,
                        “placement”: “After”
                      },
                      “header”: “{i18n>XML}”,
                      “template”: “sap.erp4sme.c4b.payment.accountstatements.ext.fragment.DownloadXML”,
                      “horizontalAlign”: “Center”,
                      “width”: “5em”
                    },

 

 

​ Use Case: When the user clicks the “XML” button, the application will fetch the account details and payment information from the database. It will then prepare an XML file. This XML file will be downloaded and stored in the “downloads” folder on the user’s device.The screenshot below shows the CAP application UI, which includes a custom button to trigger XML generation and download.The downloaded XML looks like the screenshot below.Implementation Steps:Define a Virtual field in serviceHandler logic to generate and download xmlImporting necessary modulesImplementing the Read HandlerPreparing the JSON ContentConverting the JSON to XMLConverting XML data to buffer to downloadConverting “Open file” link to a button in the UIDefine a Virtual field in service:Define a virtual field at the service level with type “LargeBinary” and media type “application/xml”.entity AccountStatements {virtual null    as   xml : LargeBinary @Core.MediaType: ‘application/xml’,}This will automatically generate a hyperlink labelled “Open File” in the UI as shown in the screenshot below.Handler logic to generate and download xml:To download an account statement in XML format, first, retrieve the XML data for the specified account statement ID using a database transaction. Then, convert the XML data to a buffer. Finally, set the file name based on the account statement ID and specify the content type as XML.Importing Necessary ModulesTo handle the conversion and streaming of the XML content, you’ll require the Buffer and Readable modules from Node.js. Import these modules into your project with the following code:import { Buffer } from “buffer”;import { Readable } from “stream”;Add the “xml2js” library through package.jsonImport the library to proceed the xml conversionimport xml2js from “xml2js”;Implementing the Read Handler:In the Read handler of the entity, verify if the URL path includes “xml”. (name of the virtual field defined in your entity). If you have named the virtual field differently, ensure to check for that specific name in the URL.virtual null                as xml                  : LargeBinary @Core.MediaType: ‘application/xml’,If the URL path contains the specified virtual field name(url.includes(“xml”)), proceed to generate the XML content. Convert this XML content into buffer data, push the buffered data into a Readable stream, and return it. Make sure to include the media content type and the disposition filename in the response.Example:/dch/ui/payment/accountstatements/AccountStatements(${object.ID})/xmlPreparing the JSON content :Construct the json content with the retrieved data from the database.private buildAccountStatementJson(    accStmtEntries: Camt053Ntry[],    AccountStatement: RetrieveAccountStatementDetails  ) {    const jsoncontent = {      $: {        xmlns: “urn:iso:std:iso:20022:tech:xsd:camt.053.001.12”,        “xmlns:xsi”: “http://www.w3.org/2001/XMLSchema-instance”,        “xsi:schemaLocation”: “urn:iso:std:iso:20022:tech:xsd:camt.053.001.12 camt.053.001.12.xsd”      },      BkToCstmrStmt: {        GrpHdr: {          MsgId: String(AccountStatement.accountStatementDisplayId),          CreDtTm: AccountStatement.accountStatementCreatedAt        },        Stmt: {          Id: String(AccountStatement.accountStatementDisplayId),          LglSeqNb: String(AccountStatement.accountStatementDisplayId),          CreDtTm: AccountStatement.accountStatementCreatedAt,          Acct: {            Id: {              Othr: {                Id: AccountStatement.accountDisplayId              }            }          }        }      }    };    return jsoncontent;  }Converting the JSON to XML:Convert the JSON content to XML format by passing the JSON data to the xml2js library function, enabling the download of the data as an XML file.private jsonToCamt053Xml(jsondata: Camt053Document) {    const builder = new xml2js.Builder({      headless: true,      renderOpts: {        pretty: false // Set to true for pretty formatting      }    });    let camt053Xml = {      Document: jsondata    };    camt053Xml = builder.buildObject(camt053Xml);    return camt053Xml;  }Converting XML data to buffer to download:Adding the XML data to a buffer is necessary because buffers handle raw binary data, which is useful for file operations such as downloading. Converting the XML string to a buffer ensures that the data is correctly encoded and can be managed efficiently for creating the downloadable file. This step is crucial for maintaining data integrity during the download process.        BufferData = Buffer.from(xmlData.xmlString, “utf8”);        fileName = `AccountStatement${xmlData.accountStatementDisplayId}.xml`;        contentType = “application/xml”;Converting “Open File” link to a button in the UI: Define a UI Fragment that contains a button with the handler in app/webapp/ext/fragment section.<core:FragmentDefinition xmlns:core=”sap.ui.core” xmlns=”sap.m”>    <Button        core:require=”{ handler: ‘sap/erp4sme/c4b/payment/accountstatements/ext/fragment/DownloadXML’}”        text=”XML”        press=”handler.onPress” /></core:FragmentDefinition>Create a handler for the button that, when pressed, displays a toast message saying “DownloadingXML” and then redirects the browser to the XML URL.sap.ui.define([    “sap/m/MessageToast”], function (MessageToast) {    ‘use strict’;    return {        onPress: function (oEvent) {            const view = this.editFlow.getView();            const i18nModelBundle = view.getModel(“i18n”).getResourceBundle();            MessageToast.show(i18nModelBundle.getText(“DownloadingXml”));            const object = oEvent.getSource().getBindingContext().getObject();            sap.m.URLHelper.redirect(`/dch/ui/payment/accountstatements/AccountStatements(${object.ID})/xml`, true);        }    };});Add the fragment in the columns section in manifest.json.                   “DownloadXML”: {                      “position”: {                        “anchor”: “DataField::createdAt”,                        “placement”: “After”                      },                      “header”: “{i18n>XML}”,                      “template”: “sap.erp4sme.c4b.payment.accountstatements.ext.fragment.DownloadXML”,                      “horizontalAlign”: “Center”,                      “width”: “5em”                    },    Read More Technology Blogs by SAP articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author

+ There are no comments

Add yours