PHP脚本将XML转换为CSV,列标题在某些标题和行上返回空白

时间:2014-12-31 23:55:37

标签: php xml csv header fputcsv

我的完整XML在下面命名为user.xml:

<?xml version="1.0" encoding="utf-8"?>
<users>
                <user id='1'>
                                <user_name>John</user_name>
                                <user_lastname>Doe</user_lastname>
                                <previous_requisitions>
                                                <requisition_code>X321</requisition_code>
                                                <requisition_code>Y321</requisition_code>
                                                <requisition_code>Z321</requisition_code>
                                </previous_requisitions>
                                <user_requisition>
                                                <requisition_code>X123</requisition_code>
                                                <requisition_title>Ssr Dev 1</requisition_title>
                                                <requisition_relocation>10~20%</requisition_relocation>
                                </user_requisition>
                </user>
                <user id='2'>
                                <user_name>James</user_name>
                                <user_lastname>Smith</user_lastname>
                                <previous_requisitions>
                                                <requisition_code>X222</requisition_code>
                                                <requisition_code>Y222</requisition_code>
                                                <requisition_code>Z222</requisition_code>
                                </previous_requisitions>
                                <user_requisition>
                                                <requisition_code>Y123</requisition_code>
                                                <requisition_title>Sr Dev 1</requisition_title>
                                                <requisition_relocation>20~30%</requisition_relocation>
                                </user_requisition>
                </user>
                <user id='3'>
                                <user_name>Jess</user_name>
                                <user_lastname>Ssej</user_lastname>
                                <previous_requisitions>
                                                <requisition_code>X111</requisition_code>
                                                <requisition_code>Y111</requisition_code>
                                                <requisition_code>Z111</requisition_code>
                                </previous_requisitions>
                                <user_requisition>
                                                <requisition_code>Z123</requisition_code>
                                                <requisition_title>Jr Dev 2</requisition_title>
                                                <requisition_relocation>0~10%</requisition_relocation>
                                </user_requisition>
                </user>
</users>

因为在XML和借助在线资源和stackoverflow的帮助下不是很好;能够创建这个PHP脚本:

<?php
$file='user.xml';
if (file_exists($file)) {
    $xml = simplexml_load_file($file);
    $f = fopen('user.csv', 'w');
    // array to hold the field names
    $headers = array(); 
    // loop through the first set of fields to get names
    foreach ($xml->user->children() as $field) { 
        // put the field name into array
        $headers[] = $field->getName(); 
    }
    // print headers to CSV
    fputcsv($f, $headers, ',', '"');
    foreach ($xml->user as $users) {
        fputcsv($f, get_object_vars($users), ',', '"');
    }
    fclose($f);
}
?>

当我运行PHP脚本时,文件被写入但有一些问题:

  1. 并非所有标题都已写入。
  2. 并非所有数据都已写入
  3. 第一列填充了&#34;数组&#34;
  4. 请参阅下面的结果屏幕截图(在excel中打开): screenshot of result opened in excel

    问题:

    1. 因为看起来$ headers = array();没有正确循环。 PHP脚本上的任何修复都能正确显示XML中的所有标题和数据吗?

    2. 我只需要将以下子节点导出为CSV,因此这6个也将是标题:

      USER_ID,

      USER_NAME,

      user_lastname,

      user_requisition_code,

      user_requisition_title,

      user_requisition_relocation,

    3. 请指导我如何制作剧本&#34; selective&#34;省略或不包括XML中的其他子节点,如:

      previous_requisitions

      requisition_code

      1. 有没有办法让PHP(不使用BASH,wk或sed)工作或写一个&#34;管道分隔文件&#34; 而不是来自XML的CSV,假设使用相同的上面给出的XML文件。我尝试用PIPE字符替换脚本中的逗号,浏览器输出给出了这个警告: 警告:fputcsv()期望参数1为resource,boolean
      2. 非常感谢帮助和新年快乐!

        旁注:将坚持使用开源脚本,希望没有人会回答建议使用专有软件。

1 个答案:

答案 0 :(得分:0)

对于1.)你可能觉得$ headers = array();没有正确循环,但我没有看到循环有任何问题。它完全没问题,完全遵循SimpleXML记录的内容:http://php.net/book.simplexml

对于2.)我想说你想跳过分组元素中的所有同名元素(如果有的话)。这有点让我想起了这个questionanswer但是在这个例子中,具有相同名称和相同父级的兄弟姐妹被插入到其他行中。在您的情况下,您只想跳过它们。 对于您来说,通过xpath查询您正在寻找的值可能更容易。由于您查询的元素也有一个甚至是列名的名称,因此它也应该可以正常工作。 (我将在下面的示例代码中显示两种变体)

对于3.)这应该是完全可能的。你给出的错误信息让我看起来你混淆了一些其他参数。它与将管道设置为分隔符无关(代码示例将使用管道符号“|”作为CSV分隔符,它完全有效。)

话虽这么说,一个接近您在视觉中概述的问题(但缺少用户ID)的解决方案可能是:

$csv = new SplFileObject('php://output', 'w');
$csv->setCsvControl("|");

$users = iterator_to_array($xml->user, false);

foreach ($users as $index => $user) {
    $fields = [];
    foreach ($user->xpath('(*[not(./*)]|user_requisition/*)') as $field) {
        $fields[$field->getName()] = trim($field);
    }

    // first iteration output headers
    $index || $csv->fputcsv(array_keys($fields));

    $csv->fputcsv($fields);
}

示例XML的示例输出是:

user_name|user_lastname|requisition_code|requisition_title|requisition_relocation
John|Doe|X123|"Ssr Dev 1"|10~20%
James|Smith|Y123|"Sr Dev 1"|20~30%
Jess|Ssej|Z123|"Jr Dev 2"|0~10%

映射的用例是一个稍微不同的代码:

$csv = new SplFileObject('php://output', 'w');
$csv->setCsvControl("|");

$fieldDefs = [
    'user_id'                => '@id',
    'user_name'              => 'user_name',
    'user_lastname'          => 'user_lastname',
    'requisition_code'       => 'user_requisition/requisition_code',
    'requisition_title'      => 'user_requisition/requisition_title',
    'requisition_relocation' => 'user_requisition/requisition_relocation',
];

// output CSV headers
$csv->fputcsv(array_keys($fieldDefs));

$users = $xml->user;
foreach ($users as $user) {
    $fields = [];
    foreach ($fieldDefs as $fieldDef) {
        $fields[] = $user->xpath($fieldDef)[0];
    }

    $csv->fputcsv($fields);
}

输出略有不同:

user_id|user_name|user_lastname|requisition_code|requisition_title|requisition_relocation
1|John|Doe|X123|"Ssr Dev 1"|10~20%
2|James|Smith|Y123|"Sr Dev 1"|20~30%
3|Jess|Ssej|Z123|"Jr Dev 2"|0~10%

第二个示例显示了如何将更具体的映射组合到标头。这有点明确,允许以与元素名称不同的方式命名标题。

我希望这些例子是不言自明的,但你可能偶然发现我做的事情与你可能做的不同。例如。我使用 SplFileObject 来执行CSV操作,它基本上与您使用的方法相同,但是以对象的形式保持代码更干净(因为不需要重复一些)参数)。

第二个例子中的XPath表达式对你来说可能是新的,但我希望它们只是通过查看数组和你给出的XML来实现自我解释。

这些示例应该向后兼容到PHP 5.4。