任何JSON到CSV转换器

时间:2017-11-08 12:51:49

标签: php json csv

我正在尝试为JSON到CSV转换器构建通用功能。目前对于不同的json文件,我必须对现有功能进行一些更改。

当前代码:

function JsonToCSV($jfilename, $cfilename) {
        if (($json = file_get_contents($jfilename)) == false)
            die('Error reading json file...');
        $data = json_decode($json, true);
        $fp = fopen($cfilename, 'w');
        $header = false;
        foreach ($data as $row) {
            if (empty($header)) {
                $header = array_keys($row);
                fputcsv($fp, $header);
                $header = array_flip($header);
            }
            fputcsv($fp, array_merge($header, $row));
        }
        fclose($fp);
        return;
    }

以上代码适用于json

[
    {
        "Id": "1",
        "Name": "Juned Ansari",
        "Position": "Full Stack Developer",
        "Salary": "$99999"
    },
    {
        "Id": "2",
        "Name": "Mayur Marolia",
        "Position": "Data Analyst",
        "Salary": "$6789000"
    },
    {
        "Id": "3",
        "Name": "Mitesh Panchal",
        "Position": "Team Leader",
        "Salary": "$2324540"
    }
]

但问题是如果我的json结构改变了那么我必须重写上面的函数,就像它在json下面不起作用

[
    {
        "BILLING_SOFTWARE_API_KEY": "ABCD1234",
        "DISTRIBUTOR_API_KEY": "11380",
        "SALESMANS": [
            {
                "sm_code": 1,
                "sm_name": "DEEPAK MEHTA 7044524144"
            },
            {
                "sm_code": 2,
                "sm_name": "Juned Ansari"
            }
        ]
    }
]

2 个答案:

答案 0 :(得分:1)

问题是JSON是非结构化,而CSV是结构化

要清除此障碍,首先必须收集所有结构中的所有JSON字段,并且由于标头必须先写入 ,因此需要循环使用JSON两次。

$columns = [ ];
// This could be a foreach
// foreach($data as $row) { $columns = array_merge($columns, array_keys($row)); }
array_map(function($row) use (&$columns) {
    $columns = array_unique(array_merge($columns, array_keys($row)));
}, $data);
// Now columns contain all columns in all rows of the JSON.

$fp = fopen($cfilename, 'w');
fputcsv($fp, $columns);

// Set all empty fields to some default
$template = array_fill_keys($columns, '');

foreach ($data as $row) {
    fputcsv($fp, array_values(array_merge($template, $row)));
}
fclose($fp);

上述功能不适用于复杂数据(如果列有子信息,如示例中所示)。你需要一个更复杂的步骤:

foreach ($data as $row) {
    $collapsed = array_map(function($value) {
        if (is_array($value)) {
            return implode(', ', $value);
        }
        return $value;
    }, $row);
    fputcsv($fp, array_merge($template, $collapsed));
}

JSON中更复杂的信息清楚地表明你做错了。然后,最好的办法是将复数值重新编码为JSON,并将其存储在CSV字段中(使用json_encode()而不是上面的implode)。

大列名称大屠杀

对于那些你需要在糟糕的情况下抛出坏钱的情况,你可以实施我称之为Great Column Name Massacre。以最简单的形式,您可以编码

{
    "address": {
        "street": "Piazza Vieusseux",
        "number": 2,
        "locality"  : {
             "type": "city",
             "name": "Florence"
        }
    }
}

作为

[
    "address_street"          => "Piazza Vieusseux",
    "address_number"          => 2,
    "address_locality_type"   => "city",
    "address_locality_name"   => "Florence"
]

我对此有两种想法。请不要误解,但我感觉有点像你问我为什么你的Smith& Wesson电池供电的吹风机不工作,即使你把所有六个电池放在鼓里,指着你的头并扣动扳机。

我觉得我告诉你"哦,那边有一个安全开关。您需要将其从SAFE移至FIRE,否则将无法正常工作"。

所以请记住,这看起来像是一个非常糟糕的主意,我在评论中提到的折叠功能就是这个(你可以根据自己的需要调整它,见后面):

function fold($arry, $prefix = '') {
    $retval = [ ];
    foreach ($arry as $key => $value) {
        $newkey = $prefix.$key;
        if (is_array($value)) {
            $folded = fold($value, $newkey . '_');
            foreach ($folded as $subkey => $subval) {
                $retval[$subkey] = $subval;
            }
        } else {
            $retval[$newkey] = $value;
        }
    }
    return $retval;
}

一旦折叠了数组的每个元素,就可以对其进行分析以找出列名(你可以在折叠时执行此操作),然后一切都如上所述。

测试

折叠功能与提供的JSON样本一起正常工作,并产生

Array
(
    [BILLING_SOFTWARE_API_KEY] => ABCD1234
    [DISTRIBUTOR_API_KEY] => 11380
    [SALESMANS_0_sm_code] => 1
    [SALESMANS_0_sm_name] => DEEPAK MEHTA 7044524144
    [SALESMANS_1_sm_code] => 2
    [SALESMANS_1_sm_name] => Juned Ansari
)

这当然会立即引发第一个问题; " DISTRIBUTOR_API_KEY"是我们对{"DISTRIBUTOR": {"API": {"KEY": 11380}}}的期望。它有效,但解码不明确

要克服此限制,最快捷的方法是更改​​分隔符来自' _'对其他东西,或在键中以不同方式编码。

警告:这种方法不会有任何问题。如果我有时间,我会嘲笑自己幻想我可能最终得到一个与Famous Answer相匹敌的帖子。

没有时间,我特此拒绝承担所有可怕损害,财产损失,生产力损失,时间损失,失职,配偶和家庭关系异化以及严重健康后果的责任(包括但不限于我相信肯定会效仿,痛苦和长期的死亡。

答案 1 :(得分:0)

我使用简单的客户端SIDE将JSON / HTML / XML转换为CSV,EXCEL ...

因为通过将文件附加到锚标记的下载属性来轻松下载...

这是一个你可能喜欢的例子......

The JS FIDDLE

$(document).ready(function(){
    $('button').click(function(){
        var data = $('#txt').val();
        if(data == '')
            return;

        JSONToCSVConvertor(data, "Vehicle Report", true);
    });
});

function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
    //If JSONData is not an object then JSON.parse will parse the JSON string in an Object
    var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;

    var CSV = '';    
    //Set Report title in first row or line

    CSV += ReportTitle + '\r\n\n';

    //This condition will generate the Label/Header
    if (ShowLabel) {
        var row = "";

        //This loop will extract the label from 1st index of on array
        for (var index in arrData[0]) {

            //Now convert each value to string and comma-seprated
            row += index + ',';
        }

        row = row.slice(0, -1);

        //append Label row with line break
        CSV += row + '\r\n';
    }

    //1st loop is to extract each row
    for (var i = 0; i < arrData.length; i++) {
        var row = "";

        //2nd loop will extract each column and convert it in string comma-seprated
        for (var index in arrData[i]) {
            row += '"' + arrData[i][index] + '",';
        }

        row.slice(0, row.length - 1);

        //add a line break after each row
        CSV += row + '\r\n';
    }

    if (CSV == '') {        
        alert("Invalid data");
        return;
    }   

    //Generate a file name
    var fileName = "MyReport_";
    //this will remove the blank-spaces from the title and replace it with an underscore
    fileName += ReportTitle.replace(/ /g,"_");   

    //Initialize file format you want csv or xls
    var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);

    // Now the little tricky part.
    // you can use either>> window.open(uri);
    // but this will not work in some browsers
    // or you will not get the correct file extension    

    //this trick will generate a temp <a /> tag
    var link = document.createElement("a");    
    link.href = uri;

    //set the visibility hidden so it will not effect on your web-layout
    link.style = "visibility:hidden";
    link.download = fileName + ".csv";

    //this part will append the anchor tag and remove it after automatic click
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}