使用SimpleXML将多个XML文件转换为一个CSV

时间:2014-09-27 12:49:24

标签: php xml csv simplexml

我有一些xml文件,它们具有相同的元素但只有不同的信息。

第一个文件test.xml

<?xml version="1.0" encoding="UTF-8"?>
<phones>
    <phone>
        <title>"Apple iPhone 5S"</title>
        <price>
            <regularprice>500</regularprice>
            <saleprice>480</saleprice>
        </price> 
        <color>black</color>
    </phone>
</phones>

第二个文件test1.xml

<?xml version="1.0" encoding="UTF-8"?>
<phones>
    <phone>
        <title>Nokia Lumia 830</title>
        <price>
            <regularprice>400</regularprice>
            <saleprice>370</saleprice>
        </price> 
        <color>black</color>
    </phone>
</phones>

我需要将这些xml文件中的一些值转换为1个test.csv文件

所以我正在使用这个PHP代码

<?php

$filexml1='test.xml';
$filexml2='test1.xml';

    //File 1
    if (file_exists($filexml1)) {
        $xml = simplexml_load_file($filexml1); 
        $f = fopen('test.csv', 'w');

    $headers = array('title', 'color');
    $converted_array = array_map("strtoupper", $headers);


    fputcsv($f, $converted_array, ',', '"');


    foreach ($xml->phone as $phone) {

        //$phone->title = trim($phone->title, " ");
        // Array of just the components you need...
        $values = array(
           "title" => (string)$phone->title = trim(str_replace ( "\"", "&quot;", $phone->title ), " "), 
           "color" => (string)$phone->color
        );
        fputcsv($f, $values,',','"');

    }
    fclose($f); 

    echo "<p>File 1 coverted to .csv sucessfully</p>";
} else {
    exit('Failed to open test.xml.');
}

    //File 2
    if (file_exists($filexml2)) {
        $xml = simplexml_load_file($filexml2); 
        $f = fopen('test.csv', 'a');


    //the same code for second file like for the first file

    echo "<p>File 2 coverted to .csv sucessfully</p>";
} else {
    exit('Failed to open test1.xml.');
}

?>

test.csv的输出看起来就是这样

TITLE             COLOR
Apple iPhone 5S   black
Nokia Lumia 830   black

正如你所看到的,我只设法将每个文件加载到一个变量中,对于每个文件,我必须编写if语句使脚本太大,所以我想知道是否可以将所有文件加载到数组中,进程它们有一个代码块,因为xml元素是相同的并输出到一个.csv文件?基本上我只需要较少的PHP代码就需要相同的test.csv输出。

提前致谢。

2 个答案:

答案 0 :(得分:1)

在使用数组之后,PHP中还有更多可以使它变得更加简单。就像一个数组可以表示你的文件列表一样,PHP中的其他结构也是如此。

例如,由于XML文件很可能位于特定目录中,因此可以使用 GlobIterator 轻松表示:

$inputFiles = new GlobIterator(__DIR__ . '/*.xml');

然后你可以foreach对他们进行讨论,我将在另一个例子中展示。

这样的列表可以简化您的处理。这很重要,因为许多程序都有某种通用的公式:输入,处理,输出。这也称为IPO或IPO + S模型。 S代表存储。在您处理输入数据的情况下,您还将存储到新文件CSV文件中,该文件也是输出(处理完成后)。

当您遵循这样的通用模型时,您可以更轻松地构建代码,并且通过更好的结构,您通常可以获得更少的代码。即使不是,代码的每个部分都更加独立,更小,这通常是您正在寻找的。

在我在答案开头用 GlobIterator 显示的所述XML文件列表旁边,还有其他 Iterators 可以帮助处理XML数据。

例如,您有1-n个XML文件,其中包含0-n <phone>个元素。您知道要处理这些<phone>元素中的任何一个,您已经完全知道您要对它们做什么(从中提取一些数据)。那么首先列出所有XML文件中的所有<phone>元素是不是很好?

借助生成器,可以在PHP中轻松完成。这是一个可以在“运行”时多次返回值的函数。这是一个简化,更好地展示一些代码来说明这一点。假设我们已经将XML文件列表作为输入,我们希望所有<phone>元素都不在其中。当然,您可以创建所有这些<phone>元素的数组,然后再处理该数组。但是,生成器能够直接提供所有这些<phone>元素,以便在foreach循环中使用:

function extract_phones(Traversable $files) {
    foreach ($files as $file) {
        $xml = simplexml_load_file($file);
        if ($xml === false) {
            continue;
        }
        foreach ($xml->phone as $phone) {
            yield $phone;
        }
    }
}

正如此示例性生成器功能所示,它会覆盖所有$files,尝试将其加载为 SimpleXMLElement ,如果成功,则迭代所有<phone> 1}}元素和产生它们。

这意味着,如果在extract_phones内调用函数foreach,则该循环将每个<phone>元素设为 SimpleXMLElement

foreach(extract_phones($inputFiles) as $phone) {
    # $phone is a SimpleXMLElement here
}

所以现在你的问题要求创建CSV文件作为输出。这可以创建一个 SplFileObject 来传递输出并在处理时访问它。它基本上像传递文件句柄一样工作,就像在你的问题中那样但是它有更好的语义,允许稍后更容易地改变代码(你可以用另一个对象替换它)行为相同)。

此外,我已经在您的代码中看到了一些值得讨论的细节。您将引号编码为HTML实体:

 trim(str_replace( "\"", "&quot;", $phone->title ), " ")

您最有可能这样做,因为您希望在CSV文件中包含HTML实体。但是,CSV文件不需要这样。您还希望CSV文件中的数据尽可能通用。当您转换文件格式时,不应该在以后或在电子表格应用程序中的HTML上下文中使用CSV文件。我的建议就是把它留在外面用另一个地方处理。这个更属于的地方,以及稍后的地方,例如如果您使用CSV中的数据创建一些HTML。

这可以保持您的转换和数据清洁,并且还会删除处理过程中的详细位置,这不仅会使代码更复杂,而且往往是我们在程序中引入漏洞的地方。

我自己将把它从我的例子中移除。

所以让我们把它们放在一起:从所有XML文件中获取所有手机并将感兴趣的字段存储到输出CSV文件中:

$files  = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);

$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);

foreach ($phones as $phone) {
    $output->fputcsv(
        [
            $phone->title,
            $phone->color,
        ]
    );
}

然后创建您正在寻找的输出文件(没有HTML实体):

title,color
"""Apple iPhone 5S""",black
"Nokia Lumia 830",black

所有这些需求都是上面已经显示的生成器函数,它本身也具有直接的代码。其他所有东西都附带PHP。以下是完整的示例代码:

<?php
/**
 * @link http://stackoverflow.com/questions/26074850/convert-multiple-xml-files-to-csv-with-simplexml
 */

function extract_phones(Traversable $files)
{
    foreach ($files as $file) {
        $xml = simplexml_load_file($file);
        if ($xml === false) {
            continue;
        }
        foreach ($xml->phone as $phone) {
            yield $phone;
        }
    }
}

$files  = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);

$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);

foreach ($phones as $phone) {
    $output->fputcsv(
        [
            $phone->title,
            $phone->color,
        ]
    );
}

echo file_get_contents($output->getFilename());

答案 1 :(得分:0)

感谢@Ghost指出我正确的方向。所以这是我的解决方案。

<?php

$filexml = array ('test.xml', 'test1.xml');


//Headers
$fp = fopen('file.csv', 'w');

$headers = array('title', 'color');
$converted_array = array_map("strtoupper", $headers);


fputcsv($fp, $converted_array, ',', '"');


//XML
foreach ($filexml as $file) {
    if (file_exists($file)) {
        $xml = simplexml_load_file($file);

        foreach ($xml->phone as $phone) {
        $values = array(
               "title" => (string)$phone->title = trim(str_replace ( "\"", "&quot;", $phone->title ), " "), 
               "color" => (string)$phone->color
            );
            fputcsv($fp, $values, ',', '"');
        }
        echo $file . ' converted to .csv sucessfully' . '<br>';
    } else {
        echo $file . ' was not found' . '<br>';
    }


}

fclose($fp);

?>