I am trying to convert my CSV file to XML. I am using this script below which I got from a post. But it's not working on my side. Any idea why I am getting this error?
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', true);
ini_set('auto_detect_line_endings', true);
$inputFilename = 'test.csv';
$outputFilename = 'test.xml';
// Open csv to read
$inputFile = fopen($inputFilename, 'rt');
// Get the headers of the file
$headers = fgetcsv($inputFile);
// Create a new dom document with pretty formatting
$doc = new DomDocument();
$doc->formatOutput = true;
// Add a root node to the document
$root = $doc->createElement('rows');
$root = $doc->appendChild($root);
// Loop through each row creating a <row> node with the correct data
while (($row = fgetcsv($inputFile)) !== FALSE)
{
$container = $doc->createElement('row');
foreach($headers as $i => $header)
{
$child = $doc->createElement($header);
$child = $container->appendChild($child);
$value = $doc->createTextNode($row[$i]);
$value = $child->appendChild($value);
}
$root->appendChild($container);
}
$strxml = $doc->saveXML();
The error I am getting is here:
Uncaught exception 'DOMException' with message 'Invalid Character Error'
Here is my csv file:
name, email, test
john,john#foobar.com,blah
mary,mary#blah.com,something
jane,jan#something.com,blarg
bob,bob#test.com,asdfsfd
Your header row has spaces around the field names, spaces don't sit well with the names of XML elements. Simply trim() the field names...
$child = $doc->createElement(trim($header));
Related
Need help sir, I want to automatically download an XML format files from my computer or download folder.. Now its only saving on my working directory the xml converted file, Is that possible Automatically Download save after converted into XML?
<?php
// Map CSV file to array
$rows = array_map('str_getcsv', file('data.csv'));
$header = array_shift($rows);
$data = array();
foreach ($rows as $row)
{
$data[] = array_combine($header, $row);
}
// Process Data if need be
foreach($data AS $key => $val)
{
// Processing here
}
//Creates XML string and XML document using the DOM
$xml = new DomDocument('1.0', 'UTF-8');
//Add root node
$root = $xml->createElement('root');
$xml->appendChild($root);
// Add child nodes
foreach($data AS $key => $val)
{
$entry = $xml->createElement('entry');
$root->appendChild($entry);
foreach($val AS $field_name => $field_value)
{
$field_name = preg_replace("/[^A-Za-z0-9]/", '', $field_name); // preg_replace has the allowed characters
$name = $entry->appendChild($xml->createElement($field_name));
$name->appendChild($xml->createCDATASection($field_value));
}
}
// Set the formatOutput attribute of xml to true
$xml->formatOutput = true;
// Output to screen
//header('Content-Type: text/xml');
//echo $xml->saveXML();
// Save as file
$xml->save('xml-import.xml'); // save as file
?>
DomDocument has a saveXML method which returns a string that you can pass to the browser.
http://php.net/manual/de/domdocument.savexml.php
$str_xml = $xml->saveXML();
echo $str_xml;
I've got the following problem:
I have a csv file, that I can convert to XML via a php script.
This way, every field goes under the same row in the xml, and I want the product_sku field go under a different row, for example call it SKU_row.
The CSV looks like this:
my CSV file
The XML looks like this:
my XML file
PHP file code that I run to convert the CSV to XML:
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', true);
ini_set('auto_detect_line_endings', true);
function PrepareXMLName($PrepareString)
{
$PrepareString = str_replace(" ","",$PrepareString);
$PrepareString = preg_replace('#\W#', '', $PrepareString);
$PrepareString = str_replace("ZSPAZESZ","",$PrepareString);
$PrepareString = strtolower($PrepareString);
return $PrepareString;
}
$inputFilename = 'faszom.csv';
$outputFilename = 'faszom.xml';
// Open csv to read
$inputFile = fopen($inputFilename, 'rt');
// Get the headers of the file
$headers = fgetcsv($inputFile);
// Create a new dom document with pretty formatting
$doc = new DomDocument();
$doc->formatOutput = true;
// Add a root node to the document
$root = $doc->createElement('rows');
$root = $doc->appendChild($root);
// Loop through each row creating a <row> node with the correct data
while (($row = fgetcsv($inputFile)) !== FALSE)
{
$container = $doc->createElement('row');
foreach ($headers as $i => $header)
{
$header = str_replace(chr(32),"_",trim($header));
$header = strtolower($header);
if($header==''){ $header = 'empty';}
$header = PrepareXMLName($header);
if(is_numeric($header)) { $header = "number-". $header; }
//echo "HERE: " . $header . "<br />";
$child = $doc->createElement($header);
$child = $container->appendChild($child);
$value = $doc->createTextNode($row[$i]);
$value = $child->appendChild($value);
}
$root->appendChild($container);
}
header("Content-type: text/xml");
$strxml = $doc->saveXML();
$handle = fopen($outputFilename, "w");
fwrite($handle, $strxml);
fclose($handle);
echo $doc->saveXML();
How can I do in this PHP file, to let it put the product_sku fields under a different row, to be it separated from the row in which customer data's are?
XML allows you to group on a further level. So instead of your flat row model:
<rows>
<row>
<product_sku>L162L</product_sku>
<order_entity_id>31</order_entity_id>
<order_customer_firstname>Teszt</order_customer_firstname>
<product_qty_ordered>1.0000</product_qty_ordered>
</row>
You can group further on depending on the context of the data, for example by order, product and customer:
<rows>
<row>
<product>
<sku>L162L</sku>
<qty_ordered>1.0000</qty_ordered>
<product>
<order>
<entity_id>31</entity_id>
<customer>
<firstname>Teszt</firstname>
</customer>
</order>
</row>
But this depends entirely of what you want and from your question I don't see any reason on why to do it this, that or any other way nor what stands in your way to do whatever you want.
Here are the codes:
$doc = new DomDocument('1.0');
// create root node
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
$signed_values = array('a' => 'eee', 'b' => 'sd', 'c' => 'df');
// process one row at a time
foreach ($signed_values as $key => $val) {
// add node for each row
$occ = $doc->createElement('error');
$occ = $root->appendChild($occ);
// add a child node for each field
foreach ($signed_values as $fieldname => $fieldvalue) {
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
}
}
// get completed xml document
$xml_string = $doc->saveXML() ;
echo $xml_string;
If I print it in the browser I don't get nice XML structure like
<xml> \n tab <child> etc.
I just get
<xml><child>ee</child></xml>
And I want to be utf-8
How is this all possible to do?
You can try to do this:
...
// get completed xml document
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$xml_string = $doc->saveXML();
echo $xml_string;
You can make set these parameter right after you've created the DOMDocument as well:
$doc = new DomDocument('1.0');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
That's probably more concise. Output in both cases is (Demo):
<?xml version="1.0"?>
<root>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
</root>
I'm not aware how to change the indentation character(s) with DOMDocument. You could post-process the XML with a line-by-line regular-expression based replacing (e.g. with preg_replace):
$xml_string = preg_replace('/(?:^|\G) /um', "\t", $xml_string);
Alternatively, there is the tidy extension with tidy_repair_string which can pretty print XML data as well. It's possible to specify indentation levels with it, however tidy will never output tabs.
tidy_repair_string($xml_string, ['input-xml'=> 1, 'indent' => 1, 'wrap' => 0]);
With a SimpleXml object, you can simply
$domxml = new DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
/* #var $xml SimpleXMLElement */
$domxml->loadXML($xml->asXML());
$domxml->save($newfile);
$xml is your simplexml object
So then you simpleXml can be saved as a new file specified by $newfile
<?php
$xml = $argv[1];
$dom = new DOMDocument();
// Initial block (must before load xml string)
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// End initial block
$dom->loadXML($xml);
$out = $dom->saveXML();
print_R($out);
Tried all the answers but none worked. Maybe it's because I'm appending and removing childs before saving the XML.
After a lot of googling found this comment in the php documentation. I only had to reload the resulting XML to make it work.
$outXML = $xml->saveXML();
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$xml->loadXML($outXML);
$outXML = $xml->saveXML();
// ##### IN SUMMARY #####
$xmlFilepath = 'test.xml';
echoFormattedXML($xmlFilepath);
/*
* echo xml in source format
*/
function echoFormattedXML($xmlFilepath) {
header('Content-Type: text/xml'); // to show source, not execute the xml
echo formatXML($xmlFilepath); // format the xml to make it readable
} // echoFormattedXML
/*
* format xml so it can be easily read but will use more disk space
*/
function formatXML($xmlFilepath) {
$loadxml = simplexml_load_file($xmlFilepath);
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($loadxml->asXML());
$formatxml = new SimpleXMLElement($dom->saveXML());
//$formatxml->saveXML("testF.xml"); // save as file
return $formatxml->saveXML();
} // formatXML
Two different issues here:
Set the formatOutput and preserveWhiteSpace attributes to TRUE to generate formatted XML:
$doc->formatOutput = TRUE;
$doc->preserveWhiteSpace = TRUE;
Many web browsers (namely Internet Explorer and Firefox) format XML when they display it. Use either the View Source feature or a regular text editor to inspect the output.
See also xmlEncoding and encoding.
This is a slight variation of the above theme but I'm putting here in case others hit this and cannot make sense of it ...as I did.
When using saveXML(), preserveWhiteSpace in the target DOMdocument does not apply to imported nodes (as at PHP 5.6).
Consider the following code:
$dom = new DOMDocument(); //create a document
$dom->preserveWhiteSpace = false; //disable whitespace preservation
$dom->formatOutput = true; //pretty print output
$documentElement = $dom->createElement("Entry"); //create a node
$dom->appendChild ($documentElement); //append it
$message = new DOMDocument(); //create another document
$message->loadXML($messageXMLtext); //populate the new document from XML text
$node=$dom->importNode($message->documentElement,true); //import the new document content to a new node in the original document
$documentElement->appendChild($node); //append the new node to the document Element
$dom->saveXML($dom->documentElement); //print the original document
In this context, the $dom->saveXML(); statement will NOT pretty print the content imported from $message, but content originally in $dom will be pretty printed.
In order to achieve pretty printing for the entire $dom document, the line:
$message->preserveWhiteSpace = false;
must be included after the $message = new DOMDocument(); line - ie. the document/s from which the nodes are imported must also have preserveWhiteSpace = false.
based on the answer by #heavenevil
This function pretty prints using the browser
function prettyPrintXmlToBrowser(SimpleXMLElement $xml)
{
$domXml = new DOMDocument('1.0');
$domXml->preserveWhiteSpace = false;
$domXml->formatOutput = true;
$domXml->loadXML($xml->asXML());
$xmlString = $domXml->saveXML();
echo nl2br(str_replace(' ', ' ', htmlspecialchars($xmlString)));
}
I'm simply wanting to add cdata to an xml node - description. My xml function is below. I have tried using bits of the following function on php.net in my function
<?php
function updateXMLFile($itemName, $description, $pageName, $imageFileName)
{
$imageSrc = "<img src='http://nicolaelvin.com/authoring/phpThumb/phpThumb.php?src=../images/" . $imageFileName . "&w=100'/>";
$id = strtolower($id = str_replace(' ', '_', $itemName));
$directLinkToItem = 'http://nicolaelvin.com/authoring/' . $pageName . '.php#' . $id;
$xml = simplexml_load_file('nicolaElvinsPortfolio.xml');
$item = $xml->channel->addChild('item');
$item->addChild('title', $itemName);
$item->addChild('pubDate', date('r'));
$item->addChild('link', $directLinkToItem);
$item->addChild('description');
$cdata->description->createCDATASection('testyfhgjhsgsdjahgs');
$item->appendChild($cdata);
///Format XML to save indented tree rather than one line
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
//Save XML to file - remove this and following line if save not desired
$dom->save('nicolaElvinsPortfolio.xml');
}
//function from php.net
function sxml_cdata($path, $string)
{
$dom = dom_import_simplexml($path);
$cdata = $dom->ownerDocument->createCDATASection($string);
$dom->appendChild($cdata);
}
?>
Try this on for size. Let me know if you have any problems with it/questions about it (FIXED).
function updateXMLFile($itemName, $description, $pageName, $imageFileName) {
// Path to file that will be used
$filePath = 'nicolaElvinsPortfolio.xml';
// Create links - don't forget to escape values appropriately with urlencode(), htmlspecialchars() etc
$imageSrc = "<img src='".htmlspecialchars('http://nicolaelvin.com/authoring/phpThumb/phpThumb.php?src=../images/'.urlencode($imageFileName).'&w=100')."'/>";
$directLinkToItem = 'http://nicolaelvin.com/authoring/'.urlencode($pageName).'.php#'.urlencode(strtolower(str_replace(' ', '_', $itemName)));
// Create the CDATA value - whatever you want this to look like
$cdata = "$description: $imageSrc";
// Create a DOMDocument
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// Load data from file into DOMDocument
if (!$dom->load($filePath)) throw new Exception("Unable to load data source file '$filePath'");
// Create the new <item> and add it to the document
$item = $dom->getElementsByTagName('channel')->item(0)->appendChild(new DOMElement('item'));
// Add the <item>'s sub elements
$item->appendChild(new DOMElement('title', $itemName));
$item->appendChild(new DOMElement('pubDate', date('r')));
$item->appendChild(new DOMElement('link', $directLinkToItem));
// Add the CDATA
$item->appendChild(new DOMElement('description'))->appendChild(new DOMCdataSection($cdata));
// Now save back to file
$dom->save($filePath);
}
N.B. this now throws an exception if DOMDocument::load() fails - don't forget to catch it!
I'm trying to print complex XML's node values using XPath, I have attached an image for helping to see the path which I need to reach (red underline).
Original XML file can be found here
I was trying something like that:
<?php
$xml = simplexml_load_file('document.xml');
echo "<strong>Using direct method...</strong><br />";
$names = $xml->xpath('/w:document/w:body/w:tbl[0]/w:tr[1]/w:tc[0]/w:p/w:r/w:t');
foreach($names as $name) {
echo "Found $name<br />";
}
?>
This method I am using to replace this node:
$file = "document.xml";
$fp = fopen($file, "rb") or die("error");
$str = fread($fp, filesize($file));
$xml = new DOMDocument();
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->loadXML($str) or die("Error");
$root = $xml->documentElement;
$fnode = $root->childNodes->item(0);
$ori = $fnode->childNodes->item(1);
$ori1 = $ori->childNodes->item(3);
$ori2 = $ori1->childNodes->item(1);
$ori3 = $ori2->childNodes->item(1);
$ori4 = $ori3->childNodes->item(1);
$ori5 = $ori4->childNodes->item(1);
$wt = $xml->createElement("w:t");
$wtText = $xml->createTextNode("".$name." ".$item."");
$wt->appendChild($wtText);
$ori4->replaceChild($wt,$ori5);
$xml->save("document.xml");
<?php
// Load XML
$doc = new DOMDocument();
$doc->load("document.xml");
// Use xpath to grab the node in question. I copied your xpath
// query as-is, assuming it was capable of targetting exactly
// the node you are trying to replace. If it returns more than
// one node, then only the first will be replaced.
// If this isn't what you want, I suggest modifying your xpath
// query to match exactly the single node you want to replace.
$xpath = new DOMXPath($doc);
$oldElement = $xpath->query("/w:document/w:body/w:tbl[0]/w:tr[1]/w:tc[0]/w:p/w:r/w:t")->item(0);
$newElement = $doc->createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t", $name . " " . $item);
// Replace old element with new element
$oldElement->parentNode->replaceChild($newElement, $oldElement);
?>