I want to convert JSON responses to CSV format. I was able to figure out how to properly generate the headers in a way where parents of a key are appended together to generate a
Is that it (have to run the snippet to see results as HTML table demo - do not know how to put it here directly) ?
function readDown(headName, arr, outRows) {
var under = [headName];
while (arr.toString().indexOf("[object") == 0
|| Array.isArray(arr)) {
var hasArray = false;
for (var i in arr) {
if (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i])) hasArray = true;
}
if (hasArray) {
for (var i in arr)
if (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i])) {
if (isNaN(i)) under[0] += '_' + i;
readDown(under[0], arr[i], outRows);
if (arr.length === undefined)
return; // empty associative (skills)
delete arr[i++];
var next = false; // are there following arrays to be groupped ?
while (i && arr[i - 1] === undefined && i < arr.length && (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i]))) { // group whole lowest array
next = true;
var idx = outRows.length - 1
if (!Array.isArray(outRows[idx][1])) {
outRows[idx][1] = [outRows[idx][1], arr[i++]];
} else outRows[idx][1].push(arr[i++]);
delete arr[i - 1];
}
if (next && i == arr.length) {
arr.length = 0; // array full of undefined members, fix length in case
return null; // and return - we are done here
}
} else { // funny never get there now
if (hasArray && isNaN(i)) under[0] += '_' + i;
under.push(arr[i]);
break;
}
} else {
if (arr.length === undefined) { // Work_skills[0], friends[0]
under.push(arr);
outRows.push(under);
} else { // tags, greetings
if(!arr.length) return; // do not keep empty greetings
for (var i in arr) under.push(arr[i]);
outRows.push(under);
}
return null; // all arrays return here
}
// unreachable next
}
}
function JSON2CSV(arr, inRows, outRows) {
var row = [];
var hasArray = false;
for (var a in arr) {
try {
if (arr[a] == null) { // Change null's to string
arr[a] = "null";
}
if (Array.isArray(arr[a]) || arr[a].toString().indexOf("[object") == 0) {
readDown(a, arr[a], outRows);
continue;
} else {
if (isNaN(a)) {
outRows.push([a, arr[a]]);
} else {
outRows.push(['#', arr[a]]);
}
}
} catch (e) {
return e;
}
}
}
function convert(json) {
var JSONtxt;
try {
JSONtxt = JSON.parse(json);
} catch (e) {
console.log(e);
return;
}
var columnsArray = [];
JSON2CSV(JSONtxt, [], columnsArray);
var maxDepth = 1; // find longest lowest array size
for (var i in columnsArray)
if (Array.isArray(columnsArray[i][1])
&& maxDepth < columnsArray[i][1].length)
maxDepth = columnsArray[i][1].length;
maxDepth++;
var csv = [];
for (var c in columnsArray) {
if (Array.isArray(columnsArray[c][1])) {
var r = 0;
for (var nd in columnsArray[c][1][r]) {
csv.push([columnsArray[c][0] + '_' + nd]); // top header
var col = csv.length - 1;
for (; r < columnsArray[c][1].length; r++) {
csv[col].push(columnsArray[c][1][r][nd]); // key's value
delete columnsArray[c][1][r][nd];
}
r = 0;
}
} else {
csv.push(columnsArray[c]);
}
while (csv[c].length < maxDepth) csv[c].push(null);
}
var csvTxt = ""; // tab separated values (copy/paste 2 XL ?)
var htmlTab = "";
for (var r = 0; r < maxDepth; r++) {
htmlTab += "";
for (var c in csv) {
htmlTab += r==0?"" + csv[c][r];
} else htmlTab += " class=empty>"
htmlTab += "";
}
csvTxt += '\r\n';
htmlTab += r==0?" ":" ";
}
htmlTab += "
";
return htmlTab;
}
var json = '{"_id": "5cfe7d3c6deeeef08ce0444b","name": "Debra Milligain","phone": "+1 (906) 432-2182","address": "676 Merit Court, Steinhatchee, Oregon, 5491","tags": ["consequat","reprehenderit","amet"],"Work": {"skills": [{"id": 0,"name": "Programming"},{"id": 1,"name": "Business"}]},"friends": [{"id": 0,"name": "Stafford Hernandez"},{"id": 1,"name": "Colleen Christensen"},{"id": 2,"name": "Barker Keith"}],"greeting": [],"favoriteFruit": "banana"}';
table {
border-spacing: 0px; /* small tricks 2 make rounded table simply or */
}
th {
text-align:left; /* centered looks ugly */
}
td.empty {
background-color:lightgray; /* mark null cells */
}
Original code without tidy modifications for the snippet:
function readDown(headName, arr, outRows) {
var under = [headName];
while (arr.toString().indexOf("[object") == 0
|| Array.isArray(arr)) {
var hasArray = false;
for (var i in arr) {
if (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i])) hasArray = true;
}
if (hasArray) {
for (var i in arr)
if (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i])) {
if (isNaN(i)) under[0] += '_' + i;
readDown(under[0], arr[i], outRows);
if (arr.length === undefined)
return; // empty associative (skills)
delete arr[i++];
var next = false; // are there following arrays to be groupped ?
while (i && arr[i - 1] === undefined && i < arr.length && (arr[i].toString().indexOf("[object") == 0
|| Array.isArray(arr[i]))) { // group whole lowest array
next = true;
var idx = outRows.length - 1
if (!Array.isArray(outRows[idx][1])) {
outRows[idx][1] = [outRows[idx][1], arr[i++]];
} else outRows[idx][1].push(arr[i++]);
delete arr[i - 1];
}
if (next && i == arr.length) {
arr.length = 0; // array full of undefined members, fix length in case
return null; // and return - we are done here
}
} else { // funny never get there now
if (hasArray && isNaN(i)) under[0] += '_' + i;
under.push(arr[i]);
break;
}
} else {
if (arr.length === undefined) { // Work_skills[0], friends[0]
under.push(arr);
outRows.push(under);
} else { // tags, greetings
if(!arr.length) return; // do not keep empty greetings
for (var i in arr) under.push(arr[i]);
outRows.push(under);
}
return null; // all arrays return here
}
// unreachable next
}
}
function JSON2CSV(arr, inRows, outRows) {
var row = [];
var hasArray = false;
for (var a in arr) {
try {
if (arr[a] == null) { // Change null's to string
arr[a] = "null";
}
if (Array.isArray(arr[a]) || arr[a].toString().indexOf("[object") == 0) {
readDown(a, arr[a], outRows);
continue;
} else {
if (isNaN(a)) {
outRows.push([a, arr[a]]);
} else {
outRows.push(['#', arr[a]]);
}
}
} catch (e) {
return e;
}
}
}
function convert(json) {
var JSONtxt;
try {
JSONtxt = JSON.parse(json);
} catch (e) {
console.log(e);
return;
}
var columnsArray = [];
JSON2CSV(JSONtxt, [], columnsArray);
var maxDepth = 1; // find longest lowest array size
for (var i in columnsArray)
if (Array.isArray(columnsArray[i][1])
&& maxDepth < columnsArray[i][1].length)
maxDepth = columnsArray[i][1].length;
maxDepth++;
var csv = [];
for (var c in columnsArray) {
if (Array.isArray(columnsArray[c][1])) {
var r = 0;
for (var nd in columnsArray[c][1][r]) {
csv.push([columnsArray[c][0] + '_' + nd]); // top header
var col = csv.length - 1;
for (; r < columnsArray[c][1].length; r++) {
csv[col].push(columnsArray[c][1][r][nd]); // key's value
delete columnsArray[c][1][r][nd];
}
r = 0;
}
} else {
csv.push(columnsArray[c]);
}
while (csv[c].length < maxDepth) csv[c].push(null);
}
var csvTxt = ""; // tab separated values (copy/paste 2 XL ?)
var htmlTab = "";
for (var r = 0; r < maxDepth; r++) {
htmlTab += "";
for (var c in csv) {
htmlTab += "";
if (c != 0) csvTxt += '\t';
if (csv[c][r] != null) {
csvTxt += csv[c][r];
htmlTab += csv[c][r];
}
htmlTab += " ";
}
csvTxt += '\r\n';
htmlTab += " ";
}
htmlTab += "
";
console.log(csvTxt);
console.log(htmlTab);
}
var json = '{"_id": "5cfe7d3c6deeeef08ce0444b","name": "Debra Milligain","phone": "+1 (906) 432-2182","address": "676 Merit Court, Steinhatchee, Oregon, 5491","tags": ["consequat","reprehenderit","amet"],"Work": {"skills": [{"id": 0,"name": "Programming"},{"id": 1,"name": "Business"}]},"friends": [{"id": 0,"name": "Stafford Hernandez"},{"id": 1,"name": "Colleen Christensen"},{"id": 2,"name": "Barker Keith"}],"greeting": [],"favoriteFruit": "banana"}';
convert(json);
And even my original JSONoperations.hta CSV export does not look so bad, but a bit rotated and includes useless 1-level numbering, anyway if I would start here ;-)
table { border-spacing: 0px; }
th { text-align:left; }
td.empty { background-color:lightgray;}
tags 0 1 2 consequat reprehenderit amet Work skills # id name 1 0 Programming 2 1 Business friends # id name 1 0 Stafford Hernandez 2 1 Colleen Christensen 3 2 Barker Keith _id name phone address favoriteFruit 5cfe7d3c6deeeef08ce0444b Debra Milligain +1 (906) 432-2182 676 Merit Court, Steinhatchee, Oregon, 5491 banana