/** * @author n1474335 [n1474335@gmail.com] * @copyright Crown Copyright 2018 * @license Apache-2.0 */ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; /** * JSON to CSV operation */ class JSONToCSV extends Operation { /** * JSONToCSV constructor */ constructor() { super(); this.name = "JSON to CSV"; this.module = "Default"; this.description = "Converts JSON data to a CSV based on the definition in RFC 4180."; this.infoURL = "https://wikipedia.org/wiki/Comma-separated_values"; this.inputType = "JSON"; this.outputType = "string"; this.args = [ { name: "Cell delimiter", type: "binaryShortString", value: "," }, { name: "Row delimiter", type: "binaryShortString", value: "\\r\\n" } ]; } /** * @param {JSON} input * @param {Object[]} args * @returns {string} */ run(input, args) { const [cellDelim, rowDelim] = args; // Record values so they don't have to be passed to other functions explicitly this.cellDelim = cellDelim; this.rowDelim = rowDelim; const self = this; try { // If the JSON is an array of arrays, this is easy if (input[0] instanceof Array) { return input .map(row => row .map(self.escapeCellContents.bind(self)) .join(cellDelim) ) .join(rowDelim) + rowDelim; } // If it's an array of dictionaries... const header = Object.keys(input[0]); return header .map(self.escapeCellContents.bind(self)) .join(cellDelim) + rowDelim + input .map(row => header .map(h => row[h]) .map(self.escapeCellContents.bind(self)) .join(cellDelim) ) .join(rowDelim) + rowDelim; } catch (err) { throw new OperationError("Unable to parse JSON to CSV: " + err.toString()); } } /** * Correctly escapes a cell's contents based on the cell and row delimiters. * * @param {string} data * @returns {string} */ escapeCellContents(data) { // Double quotes should be doubled up data = data.replace(/"/g, '""'); // If the cell contains a cell or row delimiter or a double quote, it mut be enclosed in double quotes if ( data.indexOf(this.cellDelim) >= 0 || data.indexOf(this.rowDelim) >= 0 || data.indexOf("\n") >= 0 || data.indexOf("\r") >= 0 || data.indexOf('"') >= 0 ) { data = `"${data}"`; } return data; } } export default JSONToCSV;