Преобразование нескольких файлов 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? По сути, мне нужен тот же вывод test.csv только с меньшим количеством PHP-кода.

Заранее спасибо.

0

Решение

Помимо использования массива, в PHP есть еще кое-что, что может сделать его еще проще. Как массив может представлять список ваших файлов, так и другие конструкции в PHP тоже могут.

Например, поскольку ваши файлы XML, скорее всего, находятся в определенном каталоге а также следуйте некоторому шаблону с их именем файла, они могут быть легко представлены GlobIterator:

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

Вы могли бы тогда foreach над ними, что я покажу через другой пример.

Такой список позволяет упростить обработку. Это важно, потому что для многих программ существует некая общая форма: ввод, обработка, вывод. Это также называется IPO или IPO + S Model. S обозначает хранение. В вашем случае во время обработки входных данных вы также сохраняете в новый файл CSV-файл, который также является выходным (после полной обработки).

Когда вы следуете такой общей модели, вам легче структурировать свой код, и с лучшей структурой у вас чаще всего меньше кода. Даже если нет, каждая часть вашего кода более автономна и меньше, что чаще всего является тем, что вы ищете.

Рядом с указанным списком XML-файлов я показал в начале ответа GlobIterator Есть и другие итераторы это может помочь обработать данные XML.

Например, у вас есть 1-n XML-файлов, которые содержат 0-n <phone> элементы. Вы знаете, что хотите обработать любой из этих <phone> элементы, вы уже именно так знать, что вы хотите с ними сделать (извлечь из него некоторые данные). Так что не было бы здорово иметь список всех <phone> элементы во всех XML-файлах в первую очередь?

Это может быть легко сделано в 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> элементы и доходность их.

Это означает, что если функция 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 ), " ")

Скорее всего, вы делаете это, потому что вы хотите, чтобы HTML-сущности были внутри CSV-файла. Однако файл CSV не нуждается в таком. Вы также хотите, чтобы данные в CSV-файле были как можно более общими. Будет ли файл CSV использоваться позже в контексте HTML или в приложении для работы с электронными таблицами, не должно быть проблемой при преобразовании формата файла. Мое предложение здесь, чтобы оставить это и разобраться с этим в другом месте. Место, к которому больше принадлежит это, а потом, например, если вы используете данные из 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

Другие решения

Спасибо @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);

?>
0

По вопросам рекламы [email protected]