Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Array to XML Using XDOMDocument
<?php
class XDOMElement extends DOMElement {
function __construct($name, $value = null, $namespaceURI = null) {
parent::__construct($name, null, $namespaceURI);
}
}
class XDOMDocument extends DOMDocument {
function __construct($version = null, $encoding = null) {
parent::__construct($version, $encoding);
$this->registerNodeClass('DOMElement', 'XDOMElement');
}
function createElement($name, $value = null, $namespaceURI = null) {
$element = new XDOMElement($name, $value, $namespaceURI);
$element = $this->importNode($element);
if (!empty($value)) {
$element->appendChild(new DOMText($value));
}
return $element;
}
}
class array2xml extends XDOMDocument {
public $nodeName;
private $xpath;
private $root;
private $node_name;
/**
* Constructor
* Set up the DOM environment
* @param string $root The name of the root node
* @param string $node_name
* @internal param string $nod_name The name numeric keys are called
*/
public function __construct($root = 'root', $node_name = 'node') {
parent::__construct();
/*** set the encoding ***/
$this->encoding = "ISO-8859-1";
$this->xmlVersion = "1.0";
/*** format the output ***/
$this->formatOutput = TRUE;
/*** set the node names ***/
$this->node_name = $node_name;
/*** create the root element ***/
$this->root = $this->appendChild($this->createElement($root));
$this->xpath = new DomXPath($this);
}
/*
* creates the XML representation of the array
*
* @access public
* @param array $arr The array to convert
* @aparam string $node The name given to child nodes when recursing
*
*/
public function createNode($arr, $node = NULL) {
echo 'create node:'.PHP_EOL;
if (is_null($node)) {
echo 'root node';
$node = $this->root;
}
print_r($arr);echo PHP_EOL;
if(count($arr) > 1 || is_array($arr) || is_object($arr)) foreach ($arr as $element => $value) {
$element = is_numeric($element) ? $this->node_name : $element;
$elementXMLSafe = htmlentities($element, ENT_XML1);
$elementXMLSafe = str_replace ("'", "", $elementXMLSafe);
$elementXMLSafe = str_replace (" ", "_", $elementXMLSafe);
$elementXMLSafe = preg_replace ('/[^\p{L}\p{N}]/u', '_', $elementXMLSafe);
//@TODO: Add Original element name into attribute so data is not lost
$child = $this->createElement((string)$elementXMLSafe, (is_array($value) || is_object($value)) ? NULL : (string)$value);
$node->appendChild($child);
if (is_array($value) || is_object($value)) {
self::createNode((array)$value, $child);
}
}
}
/*
* Return the generated XML as a string
*
* @access public
* @return string
*
*/
public function __toString() {
return $this->saveXML();
}
/*
* array2xml::query() - perform an XPath query on the XML representation of the array
* @param str $query - query to perform
* @return mixed
*/
public function query($query) {
return $this->xpath->evaluate($query);
}
} // end of class
$sampleObject = new stdClass();
$sampleObject->property = array('Should be converted to array ','by Array 2 XML Function');
$arrayFourLevelsDeepWithObjects = array(
1=>'handle numerical arrays',
'Level_ONE_A' => 'Easy to parse',
'Level_FOUR_BY_ONE' => array(
'Level_FOUR_BY_TWO' => array(
'Object_Test_Is_Next'=>null,
'oLevel_FOUR_BY_THREE_A_OBJECT_NESTED_IN_ARRAY'=>$sampleObject,
'Level_FOUR_BY_THREE_B' => array(
'Level_FOUR_BY_FOUR' => array(
'attribute_ONE'=>'blue',
'attribute_TWO'=>null,
'attribute_THREE'=>3,
'attribute_FOUR'=>'',
'attribute_FIVE'=>true,
'attribute_SIX'=>false
)
),
)
),
'Level_ONE_B' => 'Easy to parse too',
'HANDLE_NUMBERS_IN_ARRAYS_1'=>'Dont just crash, show this node',
'Handle Spaces'=>'Dont just crash, show this node',
'Handle (parens)'=>'Dont just crash, show this node',
);
//execute test of Array to XML Class
build_xml($arrayFourLevelsDeepWithObjects, 'object');
function build_xml($a, $node='item'){
//require_once(__DIR__.'/lib/functions.classes-array2xml.inc.php');
header('Content-Type: text/xml');
$xml = new array2xml('data', $node);
$xml->createNode( $a );
echo $xml;
exit;
}
@scarstens
Copy link
Author

scarstens commented Apr 29, 2014

Here is my "unit test" for the function/class. Expects a function "build_xml" with 2 params. First param is the data array, the second is the default name for a node (used when nodes cannot be labled due to numeric or other issues in parsing)

$sampleObject = new stdClass();
$sampleObject->property = array('Should be converted to array ','by Array 2 XML Function');
$arrayFourLevelsDeepWithObjects = array(
    'Level_ONE_A' => 'Easy to parse',
    'Level_FOUR_BY_ONE' => array(
        'Level_FOUR_BY_TWO' => array(
            'Level_FOUR_BY_THREE' => array(
                'Level_FOUR_BY_FOUR' => array(
                    'attribute_ONE'=>'blue',
                    'attribute_TWO'=>null,
                    'attribute_THREE'=>3
                )
            ),
            'Level_FOUR_BY_THREE_B_OBJECT_NESTED_IN_ARRAY'=>$sampleObject
        )
    ),
    'Level_ONE_B' => 'Easy to parse too',
    'HANDLE_NUMBERS_IN_ARRAYS_1'=>'Dont just crash, show this node',
);

//execute test of Array to XML Class
build_xml($arrayFourLevelsDeepWithObjects, 'node');

@scarstens
Copy link
Author

scarstens commented Apr 29, 2014

build xml function... but this class fails miserably:

function build_xml($a, $node='item'){
    //require_once(__DIR__.'/lib/functions.classes-array2xml.inc.php');
    header('Content-Type: text/xml');

    $xml = new array2xml('data', $node);
    $xml->createNode( $a );
    echo $xml;
    exit;
}

@scarstens
Copy link
Author

scarstens commented Apr 29, 2014

updated, now with test case built into file, works great. just need help figuring out how to set an attribute/proptery on the XML object. Not sure how to do it, but would be best to store original array key labels since we have to butcher them to stop DOMElement from breaking on the "invalid character" exception. help!?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment