Last active
February 21, 2019 10:51
-
-
Save therightstuff/ae11545548930bbfdc9f2de9a77ff2d4 to your computer and use it in GitHub Desktop.
Online JSON / CSV Converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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(' '); | |
}, 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 (".") 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 (",") 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;"> </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;"> </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 -></button> | |
<br /><br /> | |
<button id="btnCSVtoJSON" onclick="CSVtoJSON()"><- 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