javascript - JSON 到 CSV 转换 (JavaScript) : How to properly format CSV conversion

标签 javascript json csv

我想将 JSON 响应转换为 CSV 格式。我能够弄清楚如何以将 key 的父项附加在一起以生成正确的 header 的方式正确生成 header 。我遇到的问题是构建与每一行对应的数据。例如,假设我有这些名为 idtagsfriends_id 的列。以 id = 1,tags = [car, plane] friends_id = [0,1,2] 为例。它应该类似于 csv 表格格式。

+-------+-------+------------+
| id    | tags  | friends_id |
+-------+-------+------------+
| 1     | car   | 0          |
+-------+-------+------------+
| empty | plane | 1          |
+-------+-------+------------+
| empty | empty | 2          |
+-------+-------+------------+
| 2     | ...   | ...        |
+-------+-------+------------+

这是我正在处理的一些测试数据。

{
    "_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"
}

下面的代码(这是我之前遇到的问题的解决方案,我发现 here 是我用来递归地将 header 生成到映射或哈希表中并附加其相应值的代码。

var arrayOfHeaders = {};
var headerDirectory = "";
var rootLevel = true;
var temp = ""
var firstWalkthrough = true;
function traverseJSON(obj){
    for (var o in obj) {
        if (typeof obj[o] == "object") {
            //console.log("Before traversal ", o)
            //console.log("Traversing the object: ", obj[o])
            if(!isNaN(o)){
                //console.log("Current position is a number ", o)
            }else{
                console.log("Adding to directory... " , o)
                headerDirectory += (headerDirectory == "") ? o : "_" + o;
            }
            rootLevel = false;
            if (firstWalkthrough){
                firstWalkthrough = false;
                //if (o == 0) 
                rootLevel = true;
            }

            traverseJSON(obj[o]);
            rootLevel = true;
            temp = headerDirectory;
            headerDirectory = "";
        } else {
            if (rootLevel) {
                if(isNaN(o)){ 
                    headerDirectory = "";
                    //console.log("Has value and is root ", o, "and temp ", temp)
                    arrayOfHeaders[o] +=  ",\"" + obj[o] + "\"";
                }else{
                    arrayOfHeaders[headerDirectory+"_"+o] +=  ",\"" + obj[o] + "\"";
                }

            }
            else {
                //console.log("Has value and is not root ", o)
                //console.log("Current Header Directory " + headerDirectory)
                //console.log("Saved temp : ", temp)
                if(isNaN(o)){ 
                    if(headerDirectory == "") headerDirectory = temp; 
                    //arrayOfHeaders.push(headerDirectory + "_" + o)
                    arrayOfHeaders[headerDirectory + "_" + o] += ",\"" + obj[o] + "\"";
                }              
            }
        }
    }
    // console.log("Array of Headers : ", arrayOfHeaders)
}

这是来自 arrayofHeaders 的实际响应:

{ _id: 'undefined,"5cfe7d3c6deeeef08ce0444b"',
  name: 'undefined,"Debra Milligain"',
  phone: 'undefined,"+1 (906) 432-2182"',
  address: 'undefined,"676 Merit Court, Steinhatchee, Oregon, 5491"',
  tags_0: 'undefined,"consequat"',
  tags_1: 'undefined,"reprehenderit"',
  tags_2: 'undefined,"amet"',
  'Work_skills_id-skill': 'undefined,"0","Business"',
  'Work_skills_name-skill': 'undefined,"Programming"',
  'friends_id-friends': 'undefined,"0","1","2"',
  'friends_name-friends':
   'undefined,"Stafford Hernandez","Colleen Christensen","Barker Keith"',
  favoriteFruit: 'undefined,"banana"' }

我想以一种易于循环并生成这样的 csv 的方式来构建数据。问题是我不确定如何从上面的内容跳到下面的内容。

+--------------------------+-----------------+-------------------+---------------------------------------------+---------------+------------------------+--------------------------+---------------------+-----------------------+---------------+
| _id                      | name            | phone             | address                                     | tags          | Work__skills__id-skill | Work__skills__name-skill | friends__id-friends | friends__name-friends | favoriteFruit |
+--------------------------+-----------------+-------------------+---------------------------------------------+---------------+------------------------+--------------------------+---------------------+-----------------------+---------------+
| 5cfe7d3c6deeeef08ce0444b | Debra Milligain | +1 (906) 432-2182 | 676 Merit Court, Steinhatchee, Oregon, 5491 | consequat     | 0                      | Programming              | 0                   | Stafford Hernandez    | banana        |
+--------------------------+-----------------+-------------------+---------------------------------------------+---------------+------------------------+--------------------------+---------------------+-----------------------+---------------+
|                          |                 |                   |                                             | reprehenderit | Business               |                          | 1                   | Colleen Christensen   |               |
+--------------------------+-----------------+-------------------+---------------------------------------------+---------------+------------------------+--------------------------+---------------------+-----------------------+---------------+
|                          |                 |                   |                                             | amet          |                        |                          | 2                   | Barker Keith          |               |
+--------------------------+-----------------+-------------------+---------------------------------------------+---------------+------------------------+--------------------------+---------------------+-----------------------+---------------+

编辑: El Tom - 可能修复您的代码

var arrayOfHeaders = {};
var headerDirectory = "";
var rootLevel = true;
var temp = ""
var firstWalkthrough = true;
traverseJSON(
    JSON.parse('{"_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"}')
)
function traverseJSON(obj) {
    for (var o in obj) {
        if (typeof obj[o] == "object") {
            //console.log("Before traversal ", o)
            //console.log("Traversing the object: ", obj[o])
            if (!isNaN(o)) {
                //console.log("Current position is a number ", o)
            } else {
                console.log("Adding to directory... ", o)
                headerDirectory += (headerDirectory == "") ? o : "_" + o;
            }
            rootLevel = false;
            if (firstWalkthrough) {
                firstWalkthrough = false;
                //if (o == 0) 
                rootLevel = true;
            }

            traverseJSON(obj[o]);
            rootLevel = true;
            temp = headerDirectory;
            headerDirectory = "";
        } else {
            if (rootLevel) {
                if (isNaN(o)) {
                    headerDirectory = "";
                    //console.log("Has value and is root ", o, "and temp ", temp)
                    if (arrayOfHeaders[o] !== undefined) {
                        arrayOfHeaders[o].push(obj[o]);
                    } else {
                        arrayOfHeaders[o] = [obj[o]];
                    }
                } else {
                    if (arrayOfHeaders[headerDirectory + "_" + o] !== undefined) {
                        arrayOfHeaders[headerDirectory + "_" + o].push(obj[o]);
                    } else {
                        arrayOfHeaders[headerDirectory + "_" + o] = [obj[o]];
                    }
                }

            }
            else {
                //console.log("Has value and is not root ", o)
                //console.log("Current Header Directory " + headerDirectory)
                //console.log("Saved temp : ", temp)
                if (isNaN(o)) {
                    if (headerDirectory == "") headerDirectory = temp;
                    //arrayOfHeaders.push(headerDirectory + "_" + o)
                    if (arrayOfHeaders[headerDirectory + "_" + o] !== undefined) {
                        arrayOfHeaders[headerDirectory + "_" + o].push(obj[o]);
                    } else {
                        arrayOfHeaders[headerDirectory + "_" + o] = [obj[o]];
                    }
                }
            }
        }
    }
}
var res = JSON.stringify(arrayOfHeaders).replace(/,"/g, ',\n"')
console.log("Easier Arrays in Array printed by JSON.stringify:\n", res)
res = {
    "_id": ["5cfe7d3c6deeeef08ce0444b"],
    "name": ["Debra Milligain"],
    "phone": ["+1 (906) 432-2182"],
    "address": ["676 Merit Court, Steinhatchee, Oregon, 5491"],
    "tags_0": ["consequat"],
    "tags_1": ["reprehenderit"],
    "tags_2": ["amet"],
    "Work_skills_id": [0, 1],
    "Work_skills_name": ["Programming",
        "Business"],
    "friends_id": [0, 1, 2],
    "friends_name": ["Stafford Hernandez",
        "Colleen Christensen",
        "Barker Keith"],
    "favoriteFruit": ["banana"]
};
var yourResult = "";
for(var i in arrayOfHeaders) {
    if(arrayOfHeaders[i].length > 1) {
       arrayOfHeaders[i] = '"' + arrayOfHeaders[i].join('","') + '"';
    } else {
        arrayOfHeaders[i] = '"' + arrayOfHeaders[i] + '"';
    }
    yourResult += i + ':' + arrayOfHeaders[i] + '\n';
}
console.log("\nProbably result you wanted to collect but invalid JSON format:\n", yourResult);

但是如果你将它们与我的答案进行比较,打印结构是相同的,但不是你手动生成的表(可能有一些错误)。

最佳答案

是吗(必须运行代码片段才能看到结果作为 HTML 表格演示 - 不知道如何直接将它放在这里)?

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 = "<TABLE border=\"1\" width=\"1500\">";
    for (var r = 0; r < maxDepth; r++) {
        htmlTab += "<TR>";
        for (var c in csv) {
            htmlTab += r==0?"<TH":"<TD"; // mind 1st header row too
            if (c != 0) csvTxt += '\t';
            if (csv[c][r] != null) {
                csvTxt += csv[c][r];
                htmlTab += ">" + csv[c][r];
            } else htmlTab += " class=empty>"
            htmlTab += "</TD>";
        }
        csvTxt += '\r\n';
        htmlTab += r==0?"</TH>":"</TR>";
    }
    htmlTab += "</TABLE>";
    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 */
}
<body onload="document.body.innerHTML=convert(json)"></body>

原始代码,没有对片段进行整洁的修改:

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 = "<TABLE border=\"1\">";
    for (var r = 0; r < maxDepth; r++) {
        htmlTab += "<TR>";
        for (var c in csv) {
            htmlTab += "<TD>";
            if (c != 0) csvTxt += '\t';
            if (csv[c][r] != null) {
                csvTxt += csv[c][r];
                htmlTab += csv[c][r];
            }
            htmlTab += "</TD>";
        }
        csvTxt += '\r\n';
        htmlTab += "</TR>";
    }
    htmlTab += "</TABLE>";
    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);

甚至我的原创JSONoperations.hta CSV 导出看起来还不错,但有点旋转并且包含无用的 1 级编号,无论如何我会从这里开始 ;-)

table { border-spacing: 0px; }
th { text-align:left; }
td.empty { background-color:lightgray;}
<TABLE border="1" width="900"><TR><TD>tags</TD><TD>0</TD><TD>1</TD><TD>2</TD></TR><TR><TD class="empty"></TD><TD>consequat</TD><TD>reprehenderit</TD><TD>amet</TD></TR><TR><TD>Work</TD><TD>skills</TD><TD>#</TD><TD>id</TD><TD>name</TD></TR><TR><TD class="empty"></TD><TD class="empty"></TD><TD>1</TD><TD>0</TD><TD>Programming</TD></TR><TR><TD class="empty"></TD><TD class="empty"></TD><TD>2</TD><TD>1</TD><TD>Business</TD></TR><TR><TD>friends</TD><TD>#</TD><TD>id</TD><TD>name</TD></TR><TR><TD class="empty"></TD><TD>1</TD><TD>0</TD><TD>Stafford Hernandez</TD></TR><TR><TD class="empty"></TD><TD>2</TD><TD>1</TD><TD>Colleen Christensen</TD></TR><TR><TD class="empty"></TD><TD>3</TD><TD>2</TD><TD>Barker Keith</TD></TR><TR><TD>_id</TD><TD>name</TD><TD>phone</TD><TD>address</TD><TD>favoriteFruit</TD></TR><TR><TD>5cfe7d3c6deeeef08ce0444b</TD><TD>Debra Milligain</TD><TD>+1 (906) 432-2182</TD><TD>676 Merit Court, Steinhatchee, Oregon, 5491</TD><TD>banana</TD></TR></TABLE>

关于javascript - JSON 到 CSV 转换 (JavaScript) : How to properly format CSV conversion,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56567693/

相关文章:

json - 避免从输入/输出 JSON(CXF Web 服务)包装对象类型名称

javascript - 使用 Angular.js 显示 JsonResult

java - 将 CSV 文件转换为 2D 数组 Java

CSVtoTable 的 SQL 表值函数的性能瓶颈

javascript - 如何将选定复选框的值发送到 app.get 路由

javascript - MathJax:无法使用 Jekyll 渲染\begin{cases}...\end{cases}

php - 如何将数组中的 php 变量传递给 javascript?

c++ - 类 CSV 格式的 C 库支持多个 "tables"和“命名引用

javascript - Angular $q.all() 出现问题

javascript - 错误 : undefined is not an object (evaluating '$scope.inputcode.password' )