Skip to content

Instantly share code, notes, and snippets.

@therightstuff
Last active February 21, 2019 10:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save therightstuff/ae11545548930bbfdc9f2de9a77ff2d4 to your computer and use it in GitHub Desktop.
Save therightstuff/ae11545548930bbfdc9f2de9a77ff2d4 to your computer and use it in GitHub Desktop.
Online JSON / CSV Converter
<html>
<head>
<title>Online JSON / CSV Converter</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.js"></script>
<script language="javascript">
var conversionStyle;
var prefixConnector;
var delimiter, delimiterCharacter;
function append(id, addedText){
$('#' + id).val(function(i, text) {
return text + addedText;
});
}
function updateStyle(){
conversionStyle = $('input[name=conversionStyle]:checked').val();
localStorage.conversionStyle = conversionStyle;
prefixConnector = $('#prefixConnector').val();
localStorage.prefixConnector = prefixConnector;
delimiter = $('input[name=delimiter]:checked').val();
localStorage.delimiter = delimiter;
if (delimiter == 'tab') {
delimiterCharacter = '\t';
} else {
delimiterCharacter = $('#delimiterCharacter').val();
localStorage.delimiterCharacter = delimiterCharacter;
}
}
/*
prefixes:
login.key,value
login.title.key,value
headers:
login
,key,value
,title
,,key,value
*/
function traverseJSON(obj, prefix) {
var val, isObject, isArray;
for (var i in obj) {
val = obj[i];
// arrays are transformed to delimited values
if (val.constructor === Array) {
console.log('val is array');
val = val.join(delimiterCharacter);
}
if (val !== null && typeof(val)=="object") {
if (conversionStyle == "headers"){
append('txtCSV', prefix + i + '\n');
// step into the object
traverseJSON(obj[i], delimiterCharacter + prefix);
} else {
// step into the object
traverseJSON(obj[i], prefix + i + prefixConnector);
}
} else {
if (conversionStyle == "headers"){
append('txtCSV', prefix + i + delimiterCharacter + val + '\n');
} else {
append('txtCSV', prefix + i + delimiterCharacter + val + '\n');
}
}
}
}
function showError(source, message){
$('#txt' + source).css('border-color', 'red')
$('#err' + source).html(message);
setTimeout(function(){
$('#txt' + source).css('border-color', 'black')
$('#err' + source).html('&nbsp;');
}, 2000);
}
function JSONtoCSV(){
updateStyle();
// if prefixes and prefixConnector in JSON then error
if (conversionStyle == "prefixes" &&
$('#txtJSON').val().indexOf(prefixConnector) >= 0
){
showError('JSON', 'Cannot convert JSON containing the specified prefix connector');
return;
}
var obj;
try {
obj = JSON.parse($('#txtJSON').val());
} catch (parseException){
showError('JSON', 'Invalid JSON');
return;
}
$('#txtCSV').val('');
// we only use prefixes when converting to a single row
traverseJSON(obj, '');
saveCSV();
}
var linesCSV;
var current, linesCSVlength;
// return number of leading delimiterCharacters
function getIndentationLevel(line){
var i = 0;
var lineLength = lines[line].length;
while (i < lineLength && lines[line].charAt(i) == delimiterCharacter) {
i++;
}
return i;
}
// remove leading delimiterCharacters
function trimIndentation(){
lines[current] = lines[current].substr(getIndentationLevel(current));
}
function getValues(line){
var numValues = 0;
var val;
var delimiterIndex = line.indexOf(delimiterCharacter);
if (delimiterIndex < 0){
val = line;
} else {
val = [];
while (delimiterIndex >= 0) {
console.log(line);
val.push(line.substr(0, delimiterIndex));
line = line.substr(delimiterIndex + delimiterCharacter.length);
delimiterIndex = line.indexOf(delimiterCharacter);
}
val.push(line);
}
return val;
}
// return object with all sequential rows of given level
function traverseCSV(level){
var obj = {};
var header, key;
if (conversionStyle == "headers"){
while (current < linesCSVlength && getIndentationLevel(current) == level){
trimIndentation();
delimiterIndex = lines[current].indexOf(delimiterCharacter);
// stop processing on blank line
if (lines[current].length == 0){
return obj;
}
// if key / value, add key:value
if (delimiterIndex >= 0){
key = lines[current].substr(0, delimiterIndex);
obj[key] = getValues(lines[current].substr(delimiterIndex + delimiterCharacter.length));
//console.log(obj);
} else {
// if key only
if (delimiterIndex < 0){
header = lines[current];
// if next row is indented, add key:traverseCSV(level+1)
if (getIndentationLevel(current + 1) > level) {
current++;
obj[header] = traverseCSV(level + 1);
} else {
// report format error
throw new Error('Invalid CSV: ' + lines[current]);
return obj;
}
}
}
if (current < (linesCSVlength-1) && getIndentationLevel(current + 1) == level) {
current++;
} else {
return obj;
}
}
} else {
var currentObj;
var parent, connectorIndex;
// loop through lines, split on prefixConnector and add to existing / created object
while (current < linesCSVlength){
// use reference object for each line
var currentObj = obj;
// for each prefix connector
while (lines[current].indexOf(prefixConnector) >= 0){
connectorIndex = lines[current].indexOf(prefixConnector);
parent = lines[current].substr(0, connectorIndex);
lines[current] = lines[current].substr(connectorIndex + prefixConnector.length);
// ensure that prefix object exists
if (!currentObj[parent]){
currentObj[parent] = {};
}
// make currentObj reference the prefix object
currentObj = currentObj[parent];
console.log(parent);
}
// add key / value pair to currentObj
delimiterIndex = lines[current].indexOf(delimiterCharacter);
// if key / value, add key:value
if (delimiterIndex >= 0){
key = lines[current].substr(0, delimiterIndex);
currentObj[key] = getValues(lines[current].substr(delimiterIndex + delimiterCharacter.length));
}
current++;
}
}
current++;
return obj;
}
function CSVtoJSON(){
updateStyle();
// initialize CSV line data
lines = $('#txtCSV').val().split('\n');
linesCSVlength = lines.length;
current = 0;
try {
var obj = traverseCSV(0);
$('#txtJSON').val(JSON.stringify(obj, null, 2));
} catch (conversionException){
showError('CSV', conversionException.message);
}
saveJSON();
}
function resizeDocument(){
var windowWidth = $(window).width();
var windowHeight = $(window).height();
var buttonWidth = parseInt($('#btnJSONtoCSV').css('width'));
var textareaWidth = ((windowWidth-buttonWidth)/2) - 50;
var textareaHeight = windowHeight - 400;
$('#txtJSON').css('width', textareaWidth).css('height', textareaHeight);
$('#txtCSV').css('width', textareaWidth).css('height', textareaHeight);;
}
function saveJSON(){
localStorage.json = $('#txtJSON').val();
}
function saveCSV(){
localStorage.csv = $('#txtCSV').val();
}
$(function() {
if (localStorage.conversionStyle) {
// load previous conversion style
$('input:radio[name=conversionStyle]').val([localStorage.conversionStyle]);
$('input:radio[name=delimiter]').val([localStorage.delimiter]);
$('#prefixConnector').val(localStorage.prefixConnector);
$('#delimiterCharacter').val(localStorage.delimiterCharacter);
// load previous JSON and CSV data
$('#txtJSON').val(localStorage.json);
$('#txtCSV').val(localStorage.csv);
}
// bind to resize events
$( window ).resize(function() {
resizeDocument();
});
// initialize enlarged image widths
resizeDocument();
// save buttons
$("#btnSaveJSON").click( function() {
console.log('saving JSON');
var text = $("#txtJSON").val();
var filename = $("#filenameJSON").val();;
var blob = new Blob([text], {type: "text/plain;charset=utf-8"});
saveAs(blob, filename);
});
$("#btnSaveCSV").click( function() {
console.log('saving CSV');
var text = $("#txtCSV").val();
var filename = $("#filenameCSV").val();;
var blob = new Blob([text], {type: "text/plain;charset=utf-8"});
saveAs(blob, filename);
});
});
</script>
</head>
<body>
<h1>Online JSON / CSV Converter</h1>
Written by Adam Fisher (<a href="https://www.industrialcuriosity.com/categories/software/">Industrial Curiosity</a>)
<br /><br />
Source available from <a href="https://gist.github.com/therightstuff/ae11545548930bbfdc9f2de9a77ff2d4">https://gist.github.com/therightstuff/ae11545548930bbfdc9f2de9a77ff2d4</a>.
<br />
This page converts JSON to CSV and back. All data stored in browser's local storage only, no data is communicated once the page has loaded.
<br /><br />
For Microsoft Excel compatibility, simply use tab delimiters and save the file with the xls extension.
<h2>CSV style</h2>
<table width="100%">
<tr>
<td>
Headers
<input type="radio" name="conversionStyle" value="prefixes" checked="checked" />Prefixes attached with <input id="prefixConnector" type="text" size="2" value="." /> (<i>Period (&quot;.&quot;) recommended</i>)
<br />
<input type="radio" name="conversionStyle" value="headers"/>Indented header rows
</td>
<td>
Column delimiter
<input type="radio" name="delimiter" value="comma" checked="checked" />Character separated <input id="delimiterCharacter" type="text" size="2" value="," /> (<i>Comma (&quot;,&quot;) recommended</i>)
<br />
<input type="radio" name="delimiter" value="tab"/>Tab separated (<i>save with .xls for opening in Excel</i>)
</td>
</tr>
</table>
<br />
<hr />
<table width="100%">
<tr id="headerRow" align="center">
<td>
<h2>JSON</h2>
<input id="filenameJSON" value="download.json"/><button id="btnSaveJSON">Save to File</button>
<br />
<span id="errJSON" style="color:red;">&nbsp;</span>
</td>
<td></td>
<td>
<h2>CSV</h2>
<input id="filenameCSV" value="download.csv"/><button id="btnSaveCSV">Save to File</button>
<br />
<span id="errCSV" style="color:red;">&nbsp;</span>
</td>
</tr>
<tr align="center" valign="middle">
<td>
<textarea id="txtJSON" onkeyup="saveJSON()" onchange="saveJSON()"></textarea>
</td>
<td align="center">
<button id="btnJSONtoCSV" onclick="JSONtoCSV()">JSON to CSV -&gt;</button>
<br /><br />
<button id="btnCSVtoJSON" onclick="CSVtoJSON()">&lt;- CSV to JSON</button>
</td>
<td>
<textarea id="txtCSV" onkeyup="saveCSV()" onchange="saveCSV()"></textarea>
</td>
</tr>
</table>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment