我正在尝试为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"
}
]
}
]
答案 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 ...
因为通过将文件附加到锚标记的下载属性来轻松下载...
这是一个你可能喜欢的例子......
$(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);
}