Во-первых, я знаю, что это довольно длинный/подробный пост, если вы ищете суть моей проблемы, вы можете прыгнуть на дно, где у меня есть TL;DR. Спасибо заранее всем комментаторам
Я работаю над функцией для веб-сайта моих клиентов. У них более старая версия Microsoft Excel на MAC, которая не поддерживает.XML - в системе хранения, которую они используют.XML
Поэтому мне нужно закодировать возможность преобразования CSV в XML, но XML должен соответствовать структуре, требуемой компонентом store. Я уже закодировал функцию XML для CSV, которая работает.
Это XML-результат системы хранилища (я удалил значения для безопасности моих клиентов-клиентов):
<orders>
<order>
<order_id>38</order_id>
<order_number>000015</order_number>
<order_status>Authorized</order_status>
<order_date>0000-00-00 00:00:00</order_date>
<customer_email>[email protected]</customer_email>
<order_amount>order total</order_amount>
<base_order_amount>pre shipping order total</base_order_amount>
<shipping_type>Basic Shipping</shipping_type>
<shipping_price> $0.00</shipping_price>
<billing_first_name>Name</billing_first_name>
<billing_last_name>B</billing_last_name>
<billing_address1>PO / Add</billing_address1>
<billing_address2></billing_address2>
<billing_city>Town</billing_city>
<billing_state_province>province</billing_state_province>
<billing_country>Canada</billing_country>
<billing_postal_code>postal code</billing_postal_code>
<billing_phone></billing_phone>
<emt_quest>test</emt_quest>
<emt_answ>test</emt_answ>
<emt_answ_conf>test</emt_answ_conf>
<shipping_first_name>Name</shipping_first_name>
<shipping_last_name>B</shipping_last_name>
<shipping_address1>PO / Add</shipping_address1>
<shipping_address2></shipping_address2>
<shipping_city>Town</shipping_city>
<shipping_state_province>province</shipping_state_province>
<shipping_country>Canada</shipping_country>
<shipping_postal_code>postal code</shipping_postal_code>
<shipping_phone></shipping_phone>
<items>
<item>
<item_name>Sample Item</item_name>
<item_price>$8.00</item_price>
<item_quantity>12</item_quantity>
</item>
<item>
<item_name>Sample Item 2</item_name>
<item_price>$12.00</item_price>
<item_quantity>12</item_quantity>
</item>
</items>
</order>
Это код моей функции XML для CSV
<?php
function xml2csv($xmlFile, $xPath) {
$csvData = "";
// Load the XML file
$xml = simplexml_load_file($xmlFile);
// xpath to search
$path = $xml->order;
//get headers (xpath must match above)
$headers = get_object_vars($xml->order[0]);
// Loop through the first row to get headers
foreach($headers as $key => $value){
$csvData .= $key . ',';
}
// Trim off the extra comma
$csvData = trim($csvData, ',');
// Add an LF
$csvData .= "\n";
foreach($path as $item) {
// Loop through the elements in specificed xpath
foreach($item as $key => $value) {
//check for a second generation children of specified first generation child
if ($key == "items") {
$itemString = "";
// if first generation child has children then loop through each second gen child
foreach ($item->children() as $child) {
// loop through each xpath of second generation child
foreach($child as $value) {
// for value of each xpath of second generation child get value as out
foreach($value->children() as $out) {
//combine each value into itemString for export to .csv
$itemString .= $out . "|";
}
}
}
// place item string in csvData string and remove extra pipe
$csvData .= trim($itemString, "|");
}
//else put xpath values of first geneartion child in .csv
else {
$csvData .= trim($value) . ',';
}
}
// Trim off the extra comma
$csvData = trim($csvData, ',');
// Add an LF
$csvData .= "\n";
}
// Return the CSV data
return $csvData;
}
При вызове с заданным.XML файлом из системы хранилища он выводит следующий.CSV файл (я использовал фиктивные значения "цена товара" не случайно)
order_id,order_number,order_status,order_date,customer_email,order_amount,base_order_amount,shipping_type,shipping_price,billing_first_name,billing_last_name,billing_address1,billing_address2,billing_city,billing_state_province,billing_country,billing_postal_code,billing_phone,emt_quest,emt_answ,emt_answ_conf,medicinal_use,shipping_first_name,shipping_last_name,shipping_address1,shipping_address2,shipping_city,shipping_state_province,shipping_country,shipping_postal_code,shipping_phone,items
00,000000,Authorized,0000-00-00 00:00:00,[email protected],$00.00,$00.00,Basic Shipping,$0.00,Me,Initial,123 Some Person Street,,Personville,Prov/State,Country,postal,,test,test,test,test,test,test,test,,test,test,test,test,,item name|item price|item quantity
01,000000,Authorized,0000-00-00 00:00:00,[email protected],$00.00,$00.00,Basic Shipping,$0.00,Me,Initial,123 Some Person Street,,Personville,Prov/State,Country,postal,,test,test,test,test,test,test,test,,test,test,test,test,,item name|item price|item quantity
02,000000,Authorized,0000-00-00 00:00:00,[email protected],$00.00,$00.00,Basic Shipping,$0.00,Me,Initial,123 Some Person Street,,Personville,Prov/State,Country,postal,,test,test,test,test,test,test,test,,test,test,test,test,,item name|item price|item quantity
03,000000,Authorized,0000-00-00 00:00:00,[email protected],$00.00,$00.00,Basic Shipping,$0.00,Me,Initial,123 Some Person Street,,Personville,Prov/State,Country,postal,,test,test,test,test,test,test,test,,test,test,test,test,,item name|item price|item quantity
04,000000,Authorized,0000-00-00 00:00:00,[email protected],$00.00,$00.00,Basic Shipping,$0.00,Me,Initial,123 Some Person Street,,Personville,Prov/State,Country,postal,,test,test,test,test,test,test,test,,test,test,test,test,,item name|item price|item quantity|item name|item price|item quantity
Целью здесь является то, что мой клиент может загрузить.CSV непосредственно из системы хранилища (а не по умолчанию.XML) - справиться с этим в Excel, поскольку они должны обрабатывать их заказы, а затем загрузить этот.CSV обратно в магазин - где он автоматически преобразуется в XML, как показано выше.
Поскольку.CSV - это плоский формат, то, что я сделал, конденсировал элементы XML в простую строку.CSV, где каждое значение делится на | которые не будут использоваться ни в одном из наших разметки на сайте. Как такое item name|item price|item quantity
Вот мой код, который пытается достичь этого, я приближаюсь, но у меня есть некоторое неудобное поведение с выходом. Он выдает неопределенную ошибку на отмеченной строке $itemvalue = $doc->createTextNode($irow[$g]);
(как будто цикл работает слишком много раз), а также не дает ожидаемого результата.
function contains($substring, $string) {
$pos = strpos($string, $substring);
if($pos === false) {
// string needle NOT found in haystack
return false;
}
else {
// string needle found in haystack
return true;
}
}
function csv2xml($csvData) {
$outputFilename = 'test.xml';
// Open csv to read
$input = fopen($csvData, 'rt');
// Get the headers of the file
$headers = fgetcsv($input);
// Create a new dom document with pretty formatting
$doc = new DomDocument();
$doc->formatOutput = true;
// Add a root node to the document
$root = $doc->createElement('orders');
$root = $doc->appendChild($root);
while (($row = fgetcsv($input)) !== FALSE) {
$container = $doc->createElement('order');
foreach ($headers as $i => $header)
{
//set temp file name here
$tempFile = "temp.csv";
//prepare mockCSV
$mockCSV = "";
$mockCSV .= "item_name,item_price,item_quantity";
$mockCSV .= "\n";
//check if current property has items data with |
if (contains("|", $row[$i])) {
//if it does create array of data
$item_arr = explode("|", $row[$i]);
//create header for 'items' node
$child = $doc->createElement($header);
$child = $container->appendChild($child);
//count for items
$count = 0;
foreach($item_arr as $k => $item) {
$mockCSV .= trim($item) . ",";
if($count == 2) {
// Trim off the extra comma
$mockCSV = trim($mockCSV, ',');
// Add an LF
$mockCSV .= "\n";
}
$count++;
}
// Trim off the extra comma
$mockCSV = trim($mockCSV, ',');
// Add an LF
$mockCSV .= "\n";
//put mock CSV data in temp file
$f = fopen($tempFile, "w");
fwrite($f, $mockCSV);
fclose($f);
//get data from temp file
$iteminput = fopen($tempFile, 'rt');
//get headers from temp file
$itemheaders = fgetcsv($iteminput);
while (($irow = fgetcsv($iteminput)) !== FALSE) {
$itemchild = $doc->createElement('item');
foreach($itemheaders as $g => $itemheader) {
$subchild = $doc->createElement($itemheader);
$subchild = $itemchild->appendChild($subchild);
$itemvalue = $doc->createTextNode($irow[$g]); /* OFFSET HAPPENS HERE */
$itemvalue = $subchild->appendChild($itemvalue);
}
}
$itemchild = $child->appendChild($itemchild);
}
else {
$child = $doc->createElement($header);
$child = $container->appendChild($child);
$value = $doc->createTextNode($row[$i]);
$value = $child->appendChild($value);
}
}
$root->appendChild($container);
}
$strxml = $doc->saveXML();
$handle = fopen($outputFilename, "w");
fwrite($handle, $strxml);
fclose($handle);
}
echo csv2xml("test.csv");
?>
Ожидаемый результат должен быть таким же, как и структура XML, опубликованная выше, но вместо этого она делает следующее:
<orders>
<order>
<order_id>38</order_id>
<order_number>000015</order_number>
<order_status>Authorized</order_status>
<order_date>0000-00-00 00:00:00</order_date>
<customer_email>[email protected]</customer_email>
<order_amount>$96.00</order_amount>
<base_order_amount>$96.00</base_order_amount>
<shipping_type>Basic Shipping</shipping_type>
<shipping_price> $0.00</shipping_price>
<billing_first_name>Name</billing_first_name>
<billing_last_name>B</billing_last_name>
<billing_address1>PO / Add</billing_address1>
<billing_address2></billing_address2>
<billing_city>Town</billing_city>
<billing_state_province>province</billing_state_province>
<billing_country>Canada</billing_country>
<billing_postal_code>postal code</billing_postal_code>
<billing_phone></billing_phone>
<emt_quest>test</emt_quest>
<emt_answ>test</emt_answ>
<emt_answ_conf>test</emt_answ_conf>
<shipping_first_name>Name</shipping_first_name>
<shipping_last_name>B</shipping_last_name>
<shipping_address1>PO / Add</shipping_address1>
<shipping_address2></shipping_address2>
<shipping_city>Town</shipping_city>
<shipping_state_province>province</shipping_state_province>
<shipping_country>Canada</shipping_country>
<shipping_postal_code>postal code</shipping_postal_code>
<shipping_phone></shipping_phone>
<items>
<item>
<item_name></item_name>
<item_price></item_price>
<item_quantity></item_quantity>
</item>
</items>
</order>
И не вставлять значения для некоторых полей. Также он не повторяется для двойных записей продукта, как показано, чье исходное поле.CSV выглядит как это item name|item price|item quantity|item name|item price|item quantity
Это моя проблема, я, похоже, не могу обрабатывать поле с разделителями каналов, поэтому оно не выводится так, как ожидалось. В более ранней версии кода я получил все данные, но не создал отдельные узлы "item".
Любая помощь очень ценится, на данный момент я думаю, что это что-то простое, и мне просто нужно еще пару глаз на эту тему.
Более того, я использую очень неоднородный код здесь, я чувствую, что я не в курсе.PHP - я чувствую, что должна быть какая-то логическая проблема с тем, как я это делаю - мой способ может работать, но должно быть более упорядоченный метод. Если кто-нибудь скажет мне, что это такое, - тот ответ, который я действительно ищу.
TL: DR начинается здесь. Я пытаюсь преобразовать данные.CSV в структурированные данные.XML, используя разграничение каналов для детей поколения второго поколения и третьего поколения
Только одно поле в моем исходном элементе.CSV "элементы" содержит такую информацию - все остальные элементы представляют собой одиночную запись с одним ключом, данные похожи на это item name|item price|item quantity|item name|item price|item quantity
Так что я делаю проверку на | внутри строки.CSV, которая в настоящее время проходит через цикл, и если она обнаружена, я использую explode()
для создания массива того, что там было.
Я пробовал воссоздать mock CSV файл и помещать его в каталог temp для размещения этой информации, а затем использовать базовый CSV для XML, который работает в моей программе, чтобы поместить эти данные в документ XML Dom
Ожидаемый результат:
<items>
<item>
<item_name>Sample Item</item_name>
<item_price>$8.00</item_price>
<item_quantity>12</item_quantity>
</item>
<item>
<item_name>Sample Item 2</item_name>
<item_price>$8.00</item_price>
<item_quantity>12</item_quantity>
</item>
</items>
Результат, который я получаю:
<items>
<item>
<item_name></item_name>
<item_price></item_price>
<item_quantity></item_quantity>
</item>
</items>
Много информации, которую мне нужно найти, чтобы правильно проиллюстрировать проблему, но моя проблема проста - как я могу добиться результата, который я хочу.
Позвольте мне сначала создать резервную копию и предложить подпрограмму для CSV для XML, а затем позаботиться о элементах, передаваемых по каналам.
Некоторые комментарии:
SimpleXML
через DOM
для его простоты использования, поэтому я буду использовать его в этом примере. Конечно, это можно сделать и с DOM
.str_getcsv()
вместо fgetcsv()
, чтобы создать рабочий пример в Интернете.базовый CSV для XML
// XML: set up object
$xml = simplexml_load_string("<orders/>");
// CSV: assume CSV in $c, get it as a whole
$csv = str_getcsv($c, "\n");
// CSV: separate 1st row with field names from the following rows
$names = str_getcsv(array_shift($csv));
// CSV: parse row by row
foreach ($csv as $row) {
// CSV: combine names as keys => data as values
$row = array_combine($names, str_getcsv($row));
// XML: create new <order>
$xml_order = $xml->addChild("order");
// CSV: parse a single row
foreach ($row as $key => $value) {
// *****
// XML: create field as child of <order>
$xml_order->addChild($key, $value);
// *****
}
}
обрабатывать элементы трубопровода
следующий код заменяет строки между //*****
выше
// CSV: check for pipes, attention use strict comparison ===
if (strpos($value, "|") === false) {
// XML: no pipe, create node as a child of <order>
$xml_order->addChild($key, $value);
} else {
// CSV: pipe present, split up data
$csv_items = str_getcsv($value,"|");
// XML: create <items> node
$xml_items = $xml_order->addChild($key);
// CSV: iterate over $csv_items, each 3 elements = 1 row
// chop row after row
while (!empty($csv_items)) {
// XML: create <item> node as child of <items>
$xml_item = $xml_items->addChild("item");
// XML: create children of <item> node
$xml_item->addChild("item_name", array_shift($csv_items));
$xml_item->addChild("item_price", array_shift($csv_items));
$xml_item->addChild("item_quantity", array_shift($csv_items));
}
}
комбинировать код без комментариев
$xml = simplexml_load_string("<orders/>");
$csv = str_getcsv($c, "\n"); // assume CSV in $c
$names = str_getcsv(array_shift($csv));
foreach ($csv as $row) {
$row = array_combine($names, str_getcsv($row));
$xml_order = $xml->addChild("order");
foreach ($row as $key => $value) {
if (strpos($value, "|") === false)
$xml_order->addChild($key, $value);
else {
$csv_items = str_getcsv($value,"|");
$xml_items = $xml_order->addChild($key);
while (!empty($csv_items)) {
$xml_item = $xml_items->addChild("item");
$xml_item->addChild("item_name", array_shift($csv_items));
$xml_item->addChild("item_price", array_shift($csv_items));
$xml_item->addChild("item_quantity", array_shift($csv_items));
}
}
}
}
см. его работу: https://eval.in/368945