UNPKG

@wmfs/xml2csv

Version:

Convert XML file to CSV file

150 lines (126 loc) 3.75 kB
'use strict' const fs = require('fs-extra') const sax = require('sax') const dottie = require('dottie') const _ = require('lodash') const debug = require('debug') const endOfLine = require('os').EOL const comma = ',' const main = function (options, callback) { try { if ((options.xmlStream && options.xmlPath) || (!options.xmlStream && !options.xmlPath)) { throw (new Error('Please provide either xmlStream or xmlPath')) } if ((options.csvStream && options.csvPath) || (!options.csvStream && !options.csvPath)) { throw (new Error('Please provide either csvStream or csvPath')) } if (options.strict !== false) { options.strict = true } if (options.csvPath) { fs.ensureFileSync(options.csvPath) } const source = options.xmlStream || fs.createReadStream(options.xmlPath) const output = options.csvStream || fs.createWriteStream(options.csvPath) const saxStream = sax.createStream(options.strict) saxStream.on('error', function (err) { console.log(err) }) let count = 0 let accepting = false let currentObj let pathParts = [] let pathPartsString writeHeadersToFile(options.headerMap, output) saxStream.on( 'opentag', function (t) { if (t.name === options.rootXMLElement) { accepting = true pathParts = [] currentObj = {} } else { if (accepting) { pathParts.push(t.name) pathPartsString = pathParts.join('.') } } } ) saxStream.on( 'text', function (text) { if (accepting) { if (text.trim() !== '\n' && text.trim() !== '' && (pathPartsString || '').length > 0) { dottie.set(currentObj, pathPartsString, text) } } } ) saxStream.on( 'cdata', function (text) { if (accepting && (pathPartsString || '').length > 0) { dottie.set(currentObj, pathPartsString, text) } } ) saxStream.on( 'closetag', function (tagName) { if (tagName === options.rootXMLElement) { writeRecordToFile(currentObj, options.headerMap, output) count++ accepting = false currentObj = {} } else { pathParts.pop() } } ) saxStream.on('end', function () { output.end() callback(null, { count: count }) }) source.pipe(saxStream) } catch (err) { debug(err) callback(err) } } function writeHeadersToFile (headerMap, outputStream) { let headerString = '' for (const [idx, header] of headerMap.entries()) { const separator = (idx === headerMap.length - 1) ? endOfLine : comma headerString += header[1] + separator } outputStream.write(headerString) } function writeRecordToFile (record, headerMap, outputStream) { let recordString = '' for (const [idx, header] of headerMap.entries()) { const field = _.isObject(record[header[3]]) ? record[header[3]][header[0]] : record[header[0]] const separator = (idx === headerMap.length - 1) ? endOfLine : comma recordString += writeField(field, header[2], separator) } outputStream.write(recordString) } function writeField (field, type, separator) { if (!field) return separator const quote = type === 'string' ? '"' : '' return `${quote}${field.replace(/"/g, '""')}${quote}${separator}` } module.exports = function (options, callback) { if (typeof callback === 'function') { return main(options, callback) } return new Promise((resolve, reject) => { return main(options, (err, res) => { if (err) { reject(err) } else { resolve(res) } }) }) }