我正在尝试使用PHP SimpleXML类将某些XML文件转换为CSV。但是,我无法实现所需的结果,因为一个父级可能有多个具有相同名称的子元素。我当前的XML文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<club>
<name>Green Riders</name>
<membership>Free</membership>
<boardMember>
<name>James F.</name>
<position>CEO</position>
</boardMember>
<boardMember>
<name>Helen D.</name>
<position>Associate Director</position>
</boardMember>
</club>
<club>
<name>Broken Dice</name>
<membership>Paid</membership>
<boardMember>
<name>Patrick B.</name>
<position>CEO</position>
</boardMember>
</club>
</root>
我希望实现的CSV输出如下:
club,name,membership,boardMember>Name,boardMember>position
Green Riders,Free,James F.,CEO
Green Riders,Free,Helen D., Associate Director
Broken Dice,Paid,Patrick B., CEO
在没有将元素名称硬编码到脚本中的情况下(是否可以在任何通用XML文件上使用),是否有实现此目标的方法?
由于我将拥有超过25种XML变体,所以我真的希望这是可能的。因此为每个脚本编写专用脚本确实效率不高。 谢谢!
答案 0 :(得分:1)
由于每个子节点的数据都必须在csv中排成一列,包括根根数据,因此,您首先可以捕获并存储根数据,然后遍历子节点并打印其数据,并在它们之前加上根数据。
请检查以下代码:
$xml = simplexml_load_file("your_xml_file.xml") or die("Error: Cannot create object");
$csv_delimeter = ",";
$csv_new_line = "\n";
foreach($xml->children() as $n) {
$club_data = array();
$club_data[] = $n->name;
$club_data[] = $n->membership;
if (isset($n->boardMember)) {
foreach ($n->boardMember as $boardMember) {
$boardMember_data = $club_data;
$boardMember_data[] = $boardMember->name;
$boardMember_data[] = $boardMember->position;
echo implode($csv_delimeter, $boardMember_data).$csv_new_line;
}
}
else {
echo implode($csv_delimeter, $club_data).$csv_new_line;
}
}
在使用示例xml数据进行测试之后,它生成了以下类型的输出:
Green Riders,Free,James F.,CEO
Green Riders,Free,Helen D., Associate Director
Broken Dice,Paid,Patrick B., CEO
您可以根据自己的情况为以下项设置不同的值:
$csv_delimeter = ",";
$csv_new_line = "\n";
由于csv输出中没有严格的规则-像delimeter可以是“,”,“,”,“;”或“ |”并且新行也可以是“ \ n \ r”
代码可以即时打印出csv行,但是,如果要将csv数据保存在文件中,则不是一一写入行,更好的方法是创建整个数组并写入除非xml数据很大,否则它一次(因为磁盘访问成本很高)。您将在网上获得大量简单的php array-to-csv函数示例。
答案 1 :(得分:1)
这是不可能的。 XML是一个嵌套结构,您会错过信息。您可以为XML结构定义一些默认映射,但是这确实非常复杂。因此,手动定义映射要容易得多(耗时少)。
function readXMLAsRecords(string $xml, array $map) {
// load the xml
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
// iterate the elements defining the rows
foreach ($xpath->evaluate($map['row']) as $row) {
$line = [];
// get the field values from the current $row
foreach ($map['columns'] as $name => $expression) {
$line[$name] = $xpath->evaluate($expression, $row);
}
// return a line
yield $line;
}
}
使用DOMXpath::evaluate()
的Xpath表达式可以返回字符串。因此,我们需要一个返回boardMember
节点的表达式以及这些字段的表达式列表。
$map = [
'row' => '/root/club/boardMember',
'columns' => [
'club_name' => 'string(parent::club/name)',
'club_membership' => 'string(parent::club/membership)',
'board_member_name' => 'string(name)',
'board_member_position' => 'string(position)'
]
];
readXMLAsRecords()
返回一个生成器,您可以在其上使用foreach
$csv = fopen('php://stdout', 'w');
fputcsv($csv, array_keys($map['columns']));
foreach (readXMLAsRecords($xml, $map) as $record) {
fputcsv($csv, $record);
}
输出:
club_name,club_membership,board_member_name,board_member_position
"Green Riders",Free,"James F.",CEO
"Green Riders",Free,"Helen D.","Associate Director"
"Broken Dice",Paid,"Patrick B.",CEO