Curtis
Curtis

Reputation: 2704

Generate friendly JSON from XML in NodeJS

Currently, I'm receiving an API request that has the crazy structure to the data, I'm attempting to parse the XML part of the response to an array/JSON so I can handle it.

Here's the exact request I am receiving:

{ 
  username: 'test',
  apiaccesskey: 'aa7a8157-3c17-4b63-806f-7aeff42ae21f',
  action: 'placeimeiorder',
  requestformat: 'JSON',
  parameters:
   '<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n',
  version: '7.2' 
}

I've tried parsing using this library (xml2js) but it's generating a result like this:

let parseresult = await parser.parseStringPromise(req.body.parameters);
console.log(parseresult);

{ PARAMETERS:
   { CUSTOMFIELD: [ 'bnVsbA==' ],
     ID: [ '1' ],
     SERVICEID: [ '1' ],
     IMEI: [ '12345678910' ],
     QNT: [ '1' ],
     SERVER: [ '0' ],
     MODELID: [ '' ],
     PROVIDERID: [ '' ],
     NETWORK: [ '' ],
     PIN: [ '' ],
     KBH: [ '' ],
     MEP: [ '' ],
     PRD: [ '' ],
     TYPE: [ '' ],
     LOCKS: [ '' ],
     REFERENCE: [ '' ],
     SN: [ '' ],
     SECRO: [ '' ] } }

which is far from ideal when trying to handle, how could I change it so I could simply access individual key/values like parseresult.IMEI or parseresult.CUSTOMFIELD

Upvotes: 0

Views: 241

Answers (3)

Tuan Anh Tran
Tuan Anh Tran

Reputation: 7237

you can also use camaro, a xpath-powered template to transform the value. it looks like this

const { transform } = require('camaro')

const data = {
    username: 'test',
    apiaccesskey: 'aa7a8157-3c17-4b63-806f-7aeff42ae21f',
    action: 'placeimeiorder',
    requestformat: 'JSON',
    parameters:
        '<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n',
    version: '7.2',
}

async function main() {
    console.log(await transform(data.parameters, {
        customerField: 'PARAMETERS/CUSTOMFIELD',
        customerId: 'PARAMETERS/ID',
        serviceId: 'PARAMETERS/SERVICEID',
    }));
}

main()

output

{ customerField: 'bnVsbA==', customerId: '1', serviceId: '1' }

If you want more fields, you can just edit the template

Upvotes: 0

Michael Kay
Michael Kay

Reputation: 163352

If you want complete control over the conversion, try saxon-js (released on Node.js a couple of weeks ago, available from npm).

For example you could do it in XPath 3.1 like this:

const SaxonJS = require("saxon-js");
SaxonJS.getResource({text:xml, type:"xml"})
.then(doc => {
    SaxonJS.serialize(
      SaxonJS.XPath.evaluate(
           "map:merge(/PARAMETERS/* ! map{name(): string()})", doc),
      {method:"json", indent:true}))});

You could extend this, for example to select which fields of the XML to include in the result.

Or for even more flexibility, you could do it in XSLT3.

(Not tested.)

Upvotes: 0

codingwithmanny
codingwithmanny

Reputation: 1184

Should just be a setting.

Code:

const xml2js = require('xml2js');
const parser = new xml2js.Parser({ explicitArray: false });

const xml = "<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n";

parser.parseString(xml, (err, result) => {
    console.dir(result);
});

Reference: https://github.com/Leonidas-from-XIV/node-xml2js#options

Output:

{
  PARAMETERS: {
    CUSTOMFIELD: 'bnVsbA==',
    ID: '1',
    SERVICEID: '1',
    IMEI: '12345678910',
    QNT: '1',
    SERVER: '0',
    MODELID: '',
    PROVIDERID: '',
    NETWORK: '',
    PIN: '',
    KBH: '',
    MEP: '',
    PRD: '',
    TYPE: '',
    LOCKS: '',
    REFERENCE: '',
    SN: '',
    SECRO: ''
  }
}

Alternative: Using the async/await like you have above:

const xml2js = require('xml2js');

(async () => {
    const parser = new xml2js.Parser({ explicitArray: false });
    const xml = "<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n";

    try {
        console.log(await parser.parseStringPromise(xml))
    } catch (error) {
        console.log('ERROR', error);
    }
})();

Upvotes: 1

Related Questions