Использование PHP для преобразования XML в CSV, но с изюминкой - PullRequest
0 голосов
/ 01 июля 2018

Я пытаюсь преобразовать некоторые нужные мне XML-файлы в CSV, используя класс PHP SimpleXML. Однако я не могу достичь желаемого результата, потому что у одного родителя может быть несколько дочерних элементов с одинаковыми именами. Мой текущий 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; поэтому было бы неэффективно писать отдельный скрипт для каждого. Спасибо!

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Это не совсем возможно. 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)'
  ]
];

К CSV

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
0 голосов
/ 01 июля 2018

Так как данные каждого дочернего узла должны быть строкой в ​​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.

...