Skip to content

Instantly share code, notes, and snippets.

@DenL
Forked from pamelafox/exportjson.js
Last active May 1, 2019 22:38
Show Gist options
  • Save DenL/bd25448d90b84e5b7792 to your computer and use it in GitHub Desktop.
Save DenL/bd25448d90b84e5b7792 to your computer and use it in GitHub Desktop.
// Includes functions for exporting active sheet or all sheets as JSON object (also Python object syntax compatible).
// Tweak the makePrettyJSON_ function to customize what kind of JSON to export.
// ignore row/column if first row/column contains this key
var IGNORE_KEYS = ["ignore", "void"];
var EXPORT_FOLDER = "JsonExport";
var EXPORT_TYPE_JSON = "Json";
var EXPORT_TYPE_CSV = "Csv";
var FUNCTION_EXPORT_JSON = "exportJson";
var FUNCTION_EXPORT_CSV = "exportCsv";
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Export')
.addItem('Preview Json', 'previewJson')
.addItem('Export Json', FUNCTION_EXPORT_JSON)
.addItem('Export Csv', FUNCTION_EXPORT_CSV)
.addItem('Options...', 'optionsDialog')
.addToUi();
}
function optionsDialog() {
var htmlOutput = HtmlService
.createTemplateFromFile('options')
.evaluate()
.setWidth(250)
.setHeight(150);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Options');
}
function saveOptions(options) {
var ui = SpreadsheetApp.getUi();
//ui.alert(JSON.stringify(options));
if (!options.hasOwnProperty('exportKeyed'))
options.exportKeyed = false;
if (!options.hasOwnProperty('stopOnBlank'))
options.stopOnBlank = false;
if (!options.hasOwnProperty('encrypt'))
options.encrypt = false;
var properties = PropertiesService.getUserProperties();
properties.setProperty('options', JSON.stringify(options));
}
function getOptions() {
var properties = PropertiesService.getUserProperties();
var options = JSON.parse(properties.getProperty('options'));
if (options == null)
options = {};
if (!options.hasOwnProperty('exportKeyed'))
options.exportKeyed = false;
if (!options.hasOwnProperty('stopOnBlank'))
options.stopOnBlank = true;
if (!options.hasOwnProperty('encrypt'))
options.encrypt = true;
return options;
}
function previewJson(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var options = getOptions();
options.exportType = EXPORT_TYPE_JSON;
var rowsData = getRowsData_(sheet, options);
var json = JSON.stringify(rowsData, parseArray, 4);
return showPreview(json);
}
function showPreview(text) {
var ui = SpreadsheetApp.getUi();
ui.alert(text);
}
function exportJson(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var options = getOptions();
options.exportType = EXPORT_TYPE_JSON;
var rowsData = getRowsData_(sheet, options);
var json = JSON.stringify(rowsData, parseArray);
var fileName = sheet.getName();
// if set to encrypt
if (options.encrypt) {
json = EncryptDecrypt(json);
fileName += ".bytes";
}
else
fileName += ".json";
exportData(json, ss, fileName);
}
function exportCsv(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var options = getOptions();
options.exportType = EXPORT_TYPE_CSV;
var rowsData = getRowsData_(sheet, options);
// start convert to csv
var csv = "";
// loop through the data in the range and build a string with the csv data
if (rowsData.length > 1) {
for (var row = 0; row < rowsData.length; row++) {
for (var col = 0; col < rowsData[row].length; col++) {
// if has a quote use double quotes instead
rowsData[row][col] = rowsData[row][col].toString().replace(/\"/g, "\"\"");
// if cell data has comma, newline, or quotes need to surround it with quotes
if (rowsData[row][col].toString().indexOf(",") != -1 || rowsData[row][col].toString().indexOf("\n") != -1 || rowsData[row][col].toString().indexOf("\"\"") != -1) {
rowsData[row][col] = "\"" + rowsData[row][col] + "\"";
}
}
// join each row's columns
if (row < rowsData.length-1) {
csv += rowsData[row].join(",") + "\r\n";
}
// last row has no carriage return
else {
csv += rowsData[row];
}
}
}
var fileName = sheet.getName();
// if set to encrypt
if (options.encrypt) {
csv = EncryptDecrypt(csv);
fileName += ".bytes";
}
else
fileName += ".csv";
exportData(csv, ss, fileName);
}
function exportData(data, ss, fileName) {
// get folder to export to
var currentFolder = DriveApp.getFileById(ss.getId()).getParents();
if (currentFolder.hasNext())
currentFolder = currentFolder.next();
else
currentFolder = DriveApp.getRootFolder();
var exportFolder = currentFolder.getFoldersByName(EXPORT_FOLDER);
if (exportFolder.hasNext())
exportFolder = exportFolder.next();
else
exportFolder = currentFolder.createFolder(EXPORT_FOLDER);
// delete existing file
/* Hash file name
var fileName = Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, sheet.getName())) + ".bytes";
Logger.log(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, sheet.getName()).reduce(function(str,chr) {
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},''));
*/
var existingFiles = exportFolder.getFilesByName(fileName);
while (existingFiles.hasNext()) {
existingFiles.next().setTrashed(true);
}
var file = exportFolder.createFile(fileName, data);
showURL(file.getUrl());
}
function parseArray(key, value) {
if (value && typeof key == "string" && key.indexOf('[]') > -1) {
var splitStr = value.split(',');
var isNumber = true;
var splitNum = [];
for (var i=0; i<splitStr.length; i++) {
isNumber = isNumber && !isNaN(splitStr[i]);
if (isNumber)
splitNum.push(+splitStr[i]);
else
break;
}
if (isNumber)
return splitNum;
else
return splitStr;
}
else
return value;
}
function EncryptDecrypt(value) {
var key = SpreadsheetApp.getActiveSpreadsheet().getRange("Warning!D31").getValue();
var keyLength = key.length;
var str = [];
for (var i=0; i<value.length; i++) {
str[i] = String.fromCharCode(value.charCodeAt(i) ^ key.charCodeAt(i%keyLength))
}
return str.join('');
}
function showURL(href){
var htmlOutput = HtmlService
.createHtmlOutput('<a href="'+href+'" target="_top" download>Download</a>')
.setWidth(200)
.setHeight(50);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'File Generated');
}
// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Returns an Array of objects.
function getRowsData_(sheet, options) {
// Get frozen rows or just the top row
var headerRows = Math.max(1, sheet.getFrozenRows());
var headersRange = sheet.getRange(1, 1, headerRows, sheet.getMaxColumns());
var headers = headersRange.getValues()[0];
var dataRange = sheet.getRange(headerRows+1, 1, sheet.getMaxRows(), sheet.getMaxColumns());
var objects = getObjects_(dataRange.getValues(), headers, options);
return objects;
}
// For every row of data in data, generates an object that contains the data. Names of
// object fields are defined in keys.
// Arguments:
// - data: JavaScript 2d array
// - keys: Array of Strings that define the property names for the objects to create
function getObjects_(data, keys, options) {
var objectDicts = [];
var objectLists = [];
var keyedObjects = {};
for (var i = 0; i < data.length; ++i) {
var objectDict = {};
var objectList = [];
// skip or stop when encounter blank row key
if (isCellEmpty_(data[i][0])) {
if (options.stopOnBlank)
break;
else
continue;
}
// check to ignore row
if (IGNORE_KEYS.indexOf(data[i][0]) > -1) {
continue;
}
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
// stop when encounter blank column key
if (isCellEmpty_(keys[j])) {
if (options.stopOnBlank)
break;
else
continue;
}
// check to ignore column
if (IGNORE_KEYS.indexOf(keys[j]) > -1) {
continue;
}
var cellData = data[i][j];
objectDict[keys[j]] = cellData;
objectList.push(cellData);
hasData = true;
}
if (hasData) {
objectDicts.push(objectDict);
keyedObjects[data[i][0]] = objectDict;
objectLists.push(objectList);
}
}
if (options.exportType == EXPORT_TYPE_JSON) {
if (options.exportKeyed)
return keyedObjects;
else
return objectDicts;
}
else if (options.exportType == EXPORT_TYPE_CSV) {
// need to add the keyrow to first row of csv
var allKeys = [];
for (var i=0; i < keys.length; i++) {
if (IGNORE_KEYS.indexOf(keys[i]) > -1) {
continue;
}
if (isCellEmpty_(keys[i])) {
if (options.stopOnBlank)
break;
else
continue;
}
allKeys.push(keys[i]);
}
objectLists.splice(0, 0, allKeys);
return objectLists;
}
}
// Returns true if the cell where cellData was read from is empty.
// Arguments:
// - cellData: string
function isCellEmpty_(cellData) {
return typeof(cellData) == 'string' && cellData == "";
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? var options = getOptions(); ?>
<form onsubmit="event.preventDefault(); google.script.run.saveOptions(this)">
<input name="exportKeyed" type="checkbox" value="true" <? if (options.exportKeyed) { ?>checked<? } ?>/> Export Keyed<br />
<input name="stopOnBlank" type="checkbox" value="true" <? if (options.stopOnBlank) { ?>checked<? } ?>/> Stop on Blank<br />
<input name="encrypt" type="checkbox" value="true" <? if (options.encrypt) { ?>checked<? } ?>/> Encrypt Output<br /> <br/>
<button type="submit" value="Submit">Save</button>
</form>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment