I tried several answers from stackoverflow but all of they had problems with repeated tags which cannot be easily done within one array, and with attributes so I modified a class I got from PHP - XMLWriter write nested array to file
I did a little trick adding some special characters to they key or the array. ### for nested elements I need to place the same tag, and @@@ for attributes. Check the XML example below.
Here's the class:
<?php
/* Key of the arrays will be the tags of the XML
It's allowed to place on the keys of the arrays structure like this:
'foo attribute1=value1 attribute2=value2' => 'bar'
And it will create the attributes for that tag
*/
class XmlConstruct extends XMLWriter
{
/**
* Constructor.
* @param string $prm_rootElementName A root element's name of a current xml document
* @param string $prm_xsltFilePath Path of a XSLT file.
* @access public
* @param null
*/
public function __construct($prm_rootElementName, $prm_xsltFilePath = '')
{
$this->openMemory();
$this->setIndent(true);
$this->setIndentString("\t");
$this->startDocument('1.0', 'UTF-8');
if ($prm_xsltFilePath) {
$this->writePi('xml-stylesheet', 'type="text/xsl" href="' . $prm_xsltFilePath . '"');
}
$this->startElementWithAttrs( $prm_rootElementName );
}
/**
* Creates a XML element taking care if tye key of the array has attributes we should add to the XML
* @access private
* @param string $prm_elementName An element's name, which can contain attributes
* @return null
*/
function startElementWithAttrs( $prm_elementName )
{
// Checking if key of array has attributes
$attrArray = explode('@@@', $prm_elementName);
$tag = array_shift($attrArray);
// Only numbers tags in XML are not allowed
if ( is_numeric($tag) ) {
$tag = 'num' . $tag;
}
if ( $pos = strpos($tag, '###') ) {
// If tag has ### means we need to repeat that tag on the XML. Removing ### till the end.
$tag = substr($tag, 0, $pos);
}
$this->startElement($tag);
foreach ( $attrArray as $attr) {
list($attr_name, $attr_value) = explode( '=', trim($attr) );
$this->writeAttribute( $attr_name, $attr_value );
}
}
/**
* Set an element with a text to a current xml document.
* @access public
* @param string $prm_elementName An element's name
* @param string $prm_ElementText An element's text
* @return null
*/
public function setElement($prm_elementName, $prm_ElementText)
{
$this->startElementWithAttrs($prm_elementName);
$this->text($prm_ElementText);
$this->endElement();
}
/**
* Construct elements and texts from an array.
* The array should contain an attribute's name in index part
* and a attribute's text in value part.
* @access public
* @param array $prm_array Contains attributes and texts
* @return null
*/
public function fromArray($prm_array)
{
if ( is_array($prm_array) ) {
foreach ($prm_array as $index=>$element) {
if ( is_array($element) ) {
$this->startElementWithAttrs($index);
$this->fromArray($element);
$this->endElement();
} else {
$this->setElement($index, $element);
}
}
}
}
/**
* Return the content of a current xml document.
* @access public
* @param null
* @return string Xml document
*/
public function getDocument()
{
$this->endElement();
$this->endDocument();
return $this->outputMemory();
}
/**
* Output the content of a current xml document.
* @access public
* @param null
*/
public function output()
{
header('Content-type: text/xml');
echo $this->getDocument();
}
}
Here's a full example:
$arr['catalog@@@name=Catalog Name@@@tag=One tag'] = array(
'cd###1'=>array(
'title'=>'Empire Burlesque',
'ARTIST'=>'Bob Dylan',
'COUNTRY'=>'USA',
'COMPANY'=>'Columbia',
'PRICE'=>'10.90',
'YEAR'=>'1985'
),
'cd###2'=>array(
'title'=>'Hide your heart',
'ARTIST'=>'Bonnie Tyler',
'COUNTRY'=>'USA',
'COMPANY'=>'CBS Records',
'PRICE'=>'9.90',
'YEAR'=>'1988'
),
'cd###3@@@active=yes'=>array(
'title'=>'Greatest Hits',
'ARTIST'=>'Dolly Parton',
'COUNTRY'=>'USA',
'COMPANY'=>'RCA',
'PRICE'=>'9.90',
'YEAR'=>'1982'
),
'cd###4'=>array(
'title'=>'Still got the blues',
'ARTIST'=>'Gary Moore',
'COUNTRY'=>'UK',
'COMPANY'=>'Virgin records',
'PRICE'=>'10.20',
'YEAR'=>'1990'
),
'lastUpdate'=>'2024-01-01 12:30'
);
$XmlConstruct = new XmlConstruct('root');
$XmlConstruct->fromArray($arr);
echo $XmlConstruct->getDocument();
This it's the result:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<catalog name="Catalog Name" tag="One tag">
<cd>
<title>Empire Burlesque</title>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</cd>
<cd>
<title>Hide your heart</title>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</cd>
<cd active="yes">
<title>Greatest Hits</title>
<ARTIST>Dolly Parton</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>RCA</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1982</YEAR>
</cd>
<cd>
<title>Still got the blues</title>
<ARTIST>Gary Moore</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Virgin records</COMPANY>
<PRICE>10.20</PRICE>
<YEAR>1990</YEAR>
</cd>
<lastUpdate>2024-01-01 12:30</lastUpdate>
</catalog>
</root>