Convert PHP nested array to XML tree structure - php

I have following array in PHP and I want to convert it equivalent XML.
$data = array("VitalParameters"=>array(
"Details"=>array(
"PID"=>1234,
"OPID"=>1345
),
"Parameters"=>array(
"HR"=>112,
"SPO2"=>0
)));
Following XML tree structure which I expected
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>
I tried many things but no luck. If any more information required just comment I will give additional information.

Try Array2XML (http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes) this has worked for me.
$data = array(
"Details" => array(
"PID" => 1234,
"OPID" => 1345
),
"Parameters" => array(
"HR" => 112,
"SPO2" => 0
));
$xml = Array2XML::createXML('VitalParameters', $data)->saveXML();
echo $xml;
Output
<?xml version="1.0" encoding="UTF-8"?>
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>

This should get you on the right track, probably needs some tuning though (especially in regards to keys aswell as checking if $arr is an array/object in the first place... this was just to show you how to recursively iterate them and print the data).
Also, this snippet does not support XML attributes in any way.
<?php
$data = array("VitalParameters"=>array(
"Details"=>array(
"PID"=>1234,
"OPID"=>1345
),
"Parameters"=>array(
"HR"=>112,
"SPO2"=>0
)));
function ArrayToXML($arr, $depth = 0, $maxDepth = 2)
{
$retVal = "";
foreach($arr as $key => $value)
{
$retVal .= "<" . $key . ">";
$type = gettype($value);
switch($type)
{
case "array":
case "object":
if ($depth < $maxDepth)
$retVal .= ArrayToXml($value, $depth+1, $maxDepth);
else
$retVal .= $type; // Depth >= MaxDepth, not iterating here
break;
case "boolean":
$retVal .= $value ? "true" : "false";
break;
case "resource":
$retVal .= "Resource";
case "NULL":
$retVal .= "NULL";
default:
$retVal .= $value;
}
$retVal .= "</" . $key . ">\n";
}
return $retVal;
}
echo ArrayToXML($data);
?>
Output:
<VitalParameters><Details><PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters><HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>

I got solution like following code
<?php
header("Content-type: text/xml; charset=utf-8");
$data = array(
"Details" => array(
"PID" => "1234","OPID" => "1345"
),"Parameters" => array(
"HR" => "112","SPO2" => "0"
)
);
$domtree = new DOMDocument('1.0', 'UTF-8');
/* create the root element of the xml tree */
$xmlRoot = $domtree->createElement("VitalParameters");
/* append it to the document created */
$xmlRoot = $domtree->appendChild($xmlRoot);
foreach ($data as $key=>$value)
{
$currentElement= $domtree->createElement($key);
$currentElement= $xmlRoot->appendChild($currentElement);
if(is_array($value))
{
foreach ($value as $k=>$v)
{
$currentElement->appendChild($domtree->createElement($k,$v));
}
}
}
/* get the xml printed */
echo $domtree->saveXML();
Output
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>

I think - this is must be more simple:
function arrayToXML($arr){
$s = "";
foreach($arr as $k => $v){
if(is_array($v)){$v = arrayToXML($v);}
$s .= "<{$k}>{$v}</{$k}>";
}
return $s;}

Related

How do I generate a SOAP request with recursive capabilities?

As I haven't found a solution for testing my SOAP integration, I have decided to make my own soap requests through POST method. The method sends an XML response which works fine, but I want to make my own array-to-xml parser. So far my code is:
private function parseData($xml, $data) {
foreach ($data as $key => $row) {
var_dump($key, $row);
if (is_array($row) && count($row)) {
return $this->parseData($xml, $row);
}
$xml->addChild($key, $row);
}
return $xml;
}
private function toWSDL($call, $data) {
$root = '<data/>';
$xml = new \SimpleXMLElement($root);
$xml = $this->parseData($xml, $data);
$xmlBody = $xml->asXML();
$open = '<ns1:' . $call . '>';
$close = '</ns1:' . $call . '>';
$xmlBody = str_replace('<data>', $open, $xmlBody);
$xmlBody = str_replace('</data>', $close, $xmlBody);
$xmlBody = str_replace('<?xml version="1.0"?>', '', $xmlBody);
$request_data = <<<EOF
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://api.hostedshop.io/service.php">
<SOAP-ENV:Body>
$xmlBody
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
EOF;
return $request_data;
}
but it does not work for a soap function called "Product_Update", which takes in an array like so:
$this->call("Product_Update", [
"ProductData" => [
"Id" => 1,
"Stock" => 2
]
]);
The "ProductData" key with an array does not work with my parseData function, which is why I am writing for help.

Object to XML changing the names of the nodes

I have the object in php like this:
object(stdClass)[16]
public 'L1' =>
object(stdClass)[17]
public 'int' =>
array (size=2)
0 => int 1
1 => int 2
public 'L2' => string '52' (length=2)
public 'R' => string '2' (length=1)
Which i can convert to XML and i get:
<data>
<L1>
<int>
<node>1</node>
<node>2</node>
</int>
</L1>
<L2>52</L2>
<R>2</R>
</data>
The problem is that i want to get rid of the names of the nodes and the node . In the final i want my XML to look like this:
<data>
<param1>1</param1>
<param2>2</param2>
<param3>52</param3>
<param4>2</param4>
</data>
Can anyone suggest the way i can do it ?
Thank you in advance.
Here is the class for creating the XML:
<?php
class XMLSerializer {
// functions adopted from http://www.sean-barton.co.uk/2009/03/turning-an-array-or-object-into-xml-using-php/
public static function generateValidXmlFromObj(stdClass $obj, $node_block='data', $node_name='node') {
$arr = get_object_vars($obj);
return self::generateValidXmlFromArray($arr, $node_block, $node_name);
}
public static function generateValidXmlFromArray($array, $node_block='data', $node_name='node') {
$xml = '<?xml version="1.0" encoding="UTF-8" ?>';
$xml .= '<' . $node_block . '>';
$xml .= self::generateXmlFromArray($array, $node_name);
$xml .= '</' . $node_block . '>';
return $xml;
}
private static function generateXmlFromArray($array, $node_name) {
$xml = '';
if (is_array($array) || is_object($array)) {
foreach ($array as $key=>$value) {
if (is_numeric($key)) {
$key = $node_name;
}
$xml .= '<' . $key . '>' . self::generateXmlFromArray($value, $node_name) . '</' . $key . '>';
}
} else {
$xml = htmlspecialchars($array, ENT_QUOTES);
}
return $xml;
}
}
And the code:
header ("Content-Type:text/xml");
include 'tamo.wss.php';
include 'xml_class.php';
$user = $_GET['id'];
$client = new SoapClient("Wsdl_Service", $options);
$client->__setSoapHeaders(Array(new WsseAuthHeader("login", "password")));
$param['ns1:l0'] = $user;
$encodded = new SoapVar($param, SOAP_ENC_OBJECT);
$result = $client->GetAttributes($encodded);
$xml = XMLSerializer::generateValidXmlFromObj($result->GetAttributesResult->Result->SingleAttribute);
echo $xml;
If I've understood your problem correctly, you can solve it this way.
$index = 1;
$xml = '<data>';
foreach(get_object_vars($result->GetAttributesResult->Result->SingleAttribute) as $value) {
$xml .= '<param' . $index . '>' . $value . '</param' . $index++ . '>';
}
$xml .= '</data>';

CodeIgniter - Process Simple XML & PHP

in the controller I have _send method. This method returns something like below:
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes' ?>
<status id="555555555" date="Wed, 28 Mar 2013 12:35:00 +0300">
<id>3806712345671174984921381</id>
<id>3806712345671174984921382</id>
<id>3806712345671174984921383</id>
<id>3806712345671174984921384</id>
<state error="Unknown1">Rejected1</state>
<state error="Unknown2">Rejected2</state>
<state error="">Accepted</state>
<state error="">Accepted</state>
</status>
XML;
This method called:
$req = $this->_send('bulk',$all_phones,$this->input->post('message'));
I am unable to create array or object suitable for passing to model for inserting into DB.
Below what I have now.
$xml = new SimpleXMLElement($xmlstr);
foreach ($xml as $child) {
if ($child->getName() == 'id') {
$id[] = $child->id;
}
if ($child->getName() == 'state') {
$state[] = $child;
//$state[] = $child['error'];
}
}
return array_merge($id,$state);
I am attempting to achieve something like this array:
array(0 => array(
'id' => '3806712345671174984921381',
'state' => 'Rejected1',
'state_error' => 'Unknown1'),
1 => array( ....
Problem with error attribute with fault array_merge.
Any ideas?
You can do write this using a simple while statement.
$cnt=$xml->id->count();$i=0;
while($i<$cnt)
{
$new_arr[$i]['id']=(string)$xml->id[$i];
$new_arr[$i]['state'] = (string)$xml->state[$i];
$new_arr[$i]['state_error'] = (string)$xml->state[$i]['error'];
$i++;
}
print_r($new_arr);
Demonstration
This is how you could do it:
// Load XML
$xmlstr = '<?xml version="1.0" standalone="yes" ?>
<status id="555555555" date="Wed, 28 Mar 2013 12:35:00 +0300">
<id>3806712345671174984921381</id>
<id>3806712345671174984921382</id>
<id>3806712345671174984921383</id>
<id>3806712345671174984921384</id>
<state error="Unknown1">Rejected1</state>
<state error="Unknown2">Rejected2</state>
<state error="">Accepted</state>
<state error="">Accepted</state>
</status>';
$xml = new SimpleXMLElement($xmlstr);
// Init
$parsed_data = array();
// Parse Id
foreach ($xml->id as $id)
{
$parsed_data[] = array(
'id' => (string)$id,
'state' => '',
'state_error' => ''
);
}
// Parse State & State Error
$i = 0;
foreach ($xml->state as $state)
{
$parsed_data[$i]['state'] = (string)$state;
$parsed_data[$i]['state_error'] = (string)$state['error'];
$i++;
}
// Output
var_dump($parsed_data);
Here's the output I got:

Simple XML to parse general recordset

I am trying to find a way to iterate through an XML recordset containing a namespace. However, I don't know the field names in advance. Sample XML is below.
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.site.com/SMART/Updates">
<NewDataSet>
<record>
<FIELD1>data1</FIELD1>
<FIELD2>data2</FIELD2>
<FIELD3>data3</FIELD3>
</record>
<record>
<FIELD1>data1</FIELD1>
<FIELD2>data2</FIELD2>
<FIELD3>data3</FIELD3>
</record>
</NewDataSet>
Again, I won't know the field names in advance. I need read the namespace, find the name of the root element ("NewDataSet", in this case) and then need to get the field names and values of the individual elements. I have tried to use $xml->getname(), and $xml->xpath('\') to find the root element name, but been unable to crack it.
(as discussed in Chat)
Plain DOM functions are the best way to process XML.
Demo or code:
<?php
header('Content-Type: text/plain');
$xml = <<<END
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.site.com/SMART/Updates">
<NewDataSet>
<record>
<FIELD1>data1</FIELD1>
<FIELD2>data2</FIELD2>
<FIELD3>data3</FIELD3>
</record>
<record>
<FIELD1>data1</FIELD1>
<FIELD2>data2</FIELD2>
<FIELD3>data3</FIELD3>
</record>
</NewDataSet>
</string>
END;
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->normalize();
$dom->loadXML($xml);
echo 'Root element name: ' . $dom->firstChild->firstChild->tagName . PHP_EOL;
echo 'Number of child elements: ' . count($dom->firstChild->firstChild->childNodes) . PHP_EOL;
echo '=====' . PHP_EOL . PHP_EOL;
echo print_node($dom->firstChild->firstChild);
function print_node($node, $level = 0, $prev_level = 0) {
$result = '';
if($node->hasChildNodes()) {
foreach($node->childNodes as $subnode) {
$result .= str_repeat(' ', $level) . $node->tagName . ' =>' . PHP_EOL;
$result .= print_node($subnode, $level + 1, $level) . PHP_EOL;
}
} else {
if(trim($node->nodeValue) !== '') {
$result .= str_repeat(' ', $level) . '**Data: ' . trim($node->nodeValue) . PHP_EOL;
}
}
return $result;
}
?>
Output:
Root element name: NewDataSet
Number of child elements: 1
=====
NewDataSet =>
record =>
FIELD1 =>
**Data: data1
record =>
FIELD2 =>
**Data: data2
record =>
FIELD3 =>
**Data: data3
NewDataSet =>
record =>
FIELD1 =>
**Data: data1
record =>
FIELD2 =>
**Data: data2
record =>
FIELD3 =>
**Data: data3
Your XML is invalid, but assuming the string tag is closed after the </NewDataSet> tag:
You can get the namespaces declared in the document using getDocNamespaces().
$xml = simplexml_load_string($xmlfile);
$namespaces = $xml->getDocNamespaces(); //array of namespaces
$dataset = $xml->children(); //first child (NewDataSet)
echo $dataset->getName(); //NewDataSet
$records = $dataset->children();
$i = 0;
$result = array();
foreach ($records as $key => $value) {
foreach ($value as $fieldName => $fieldData) {
$result[$i][$fieldName] = (string)$fieldData;
}
$i++;
}
var_dump($result);
Now $result contains an array that is easier to read and contains the rows :
array(2) {
[0]=> array(3) {
["FIELD1"]=> string(5) "data1"
["FIELD2"]=> string(5) "data2"
["FIELD3"]=> string(5) "data3"
}
[1]=> array(3) {
["FIELD1"]=> string(5) "data1"
["FIELD2"]=> string(5) "data2"
["FIELD3"]=> string(5) "data3"
}
}
Looking at the chat transcript posted in another answer, it looks like the element actually contains a string which is an escaped XML document. So there is exactly one element in the outer document, called <string>. It has no children, just content. (This looks remarkably like someone using ASP.net's service-builder.)
So the step you are missing is unescaping this inner XML to treat as a new XML document:
// Parse the outer XML, which is just one <string> node
$wrapper_sx = simplexml_load_string($wrapper_xml);
// Extract the actual XML inside it
$response_xml = (string)$wrapper_sx;
// Parse that
$response_sx = simplexml_load_string($response_xml);
// Now handle the XML
$tag_name = $response_sx->getName();
foreach ( $response_sx->children() as $child )
{
// Etc
}
// see http://github.com/IMSoP/simplexml_debug
simplexml_tree($response_sx, true);
I don't actually understand what you problem is, in your said real-life XML you gave in PHP chat, there are no namespaces involved (and even if!).
Just read out the tag-name from the document element:
# echoes NewDataSet / string (depending on which XML input)
echo dom_import_simplexml($simplexml)->ownerDocument->documentElement->tagName;
If you have actually an XML document inside another XML document, you can do the following:
// load outer document
$docOuter = new DOMDocument();
$docOuter->loadXML($xmlString);
// load inner document
$doc = new DOMDocument();
$doc->loadXML($docOuter->documentElement->nodeValue);
echo "Root element is named: ", $doc->documentElement->tagName, "\n";
Or if you prefer SimpleXML:
echo "Root element is named: ",
simplexml_load_string(simplexml_load_string($xmlString))->getName()
;

creating json or xml with mysql in php too slow

I am working on a project for a lecture at the university and I am searching for a solution for more than 2 weeks now, and I just can't get it right.
We have a project where we need to generate specific JSON or XML files to visualize them later with for example D3 or Sigma.
We have a mysql database and all the code is in Javascript (as you can see with the libraries) and we use pho to get the data from the database and to get it in the right format. Here is an example xml file I tried to create with php (it's a gexf-file for the visualization with Sigma, but it's just the same as xml):
<?xml version="1.0" encoding="UTF-8"?>
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
<meta lastmodifieddate="2009-03-20">
<creator>Gexf.net</creator>
<description>A hello world! file</description>
</meta>
<graph mode="static" defaultedgetype="directed">
<nodes>
<node id="0" label="Hello" />
<node id="1" label="Word" />
</nodes>
<edges>
<edge id="0" source="0" target="1" />
</edges>
</graph>
</gexf>
And Here is my php code where I tried to create the xml:
<?php
set_time_limit(500000000);
ini_set('memory_limit', '-1');
class XmlWriter2 {
var $xml;
var $indent;
var $stack = array();
function XmlWriter($indent = ' ') {
$this->indent = $indent;
$this->xml = '<?xml version="1.0" encoding="utf-8"?>'."\n";
}
function _indent() {
for ($i = 0, $j = count($this->stack); $i < $j; $i++) {
$this->xml .= $this->indent;
}
}
function push($element, $attributes = array()) {
$this->_indent();
$this->xml .= '<'.$element;
foreach ($attributes as $key => $value) {
$this->xml .= ' '.$key.'="'.htmlentities($value).'"';
}
$this->xml .= ">\n";
$this->stack[] = $element;
}
function element($element, $content, $attributes = array()) {
$this->_indent();
$this->xml .= '<'.$element;
foreach ($attributes as $key => $value) {
$this->xml .= ' '.$key.'="'.htmlentities($value).'"';
}
$this->xml .= '>'.htmlentities($content).'</'.$element.'>'."\n";
}
function emptyelement($element, $attributes = array()) {
$this->_indent();
$this->xml .= '<'.$element;
foreach ($attributes as $key => $value) {
$this->xml .= ' '.$key.'="'.htmlentities($value).'"';
}
$this->xml .= " />\n";
}
function pop() {
$element = array_pop($this->stack);
$this->_indent();
$this->xml .= "</$element>\n";
}
function getXml() {
return $this->xml;
}
}
/*
$xml = new XmlWriter2();
$array = array(
array('monkey', 'banana', 'Jim'),
array('hamster', 'apples', 'Kola'),
array('turtle', 'beans', 'Berty'),
);
$xml->push('zoo');
foreach ($array as $animal) {
$xml->push('animal', array('species' => $animal[0]));
$xml->element('name', $animal[2]);
$xml->element('food', $animal[1]);
$xml->pop();
}
$xml->pop();
print $xml->getXml();
<?xml version="1.0" encoding="utf-8"?>
<zoo>
<animal species="monkey">
<name>Jim</name>
<food>banana</food>
</animal>
<animal species="hamster">
<name>Kola</name>
<food>apples</food>
</animal>
<animal species="turtle">
<name>Berty</name>
<food>beans</food>
</animal>
</zoo>
*/
mysql_connect("127.0.0.1", "root", "manager") or die(mysql_error());
mysql_select_db("enrondata") or die(mysql_error());
$data1 = mysql_query("SELECT DISTINCT sId FROM mailToId WHERE date
BETWEEN '01.06.2002' AND '30.06.2002' UNION SELECT DISTINCT rId FROM
mailToId WHERE date BETWEEN '01.06.2002' AND '30.06.2002'") or die
(mysql_error());
$data2 = mysql_query("SELECT sender, recipient, count(*) AS numMails
FROM mailTo WHERE date BETWEEN '01.06.2002' AND '30.06.2002' GROUP
BY sender, recipient") or die (mysql_error());
$users = array();
$id = 0;
while($tmpUsers = mysql_fetch_array($data1)){
$tmpArray['id'] = $tmpUsers['sId'];
$user = mysql_query("SELECT email FROM users WHERE id=".$tmpUsers['sId']);
while($tmpUser = mysql_fetch_array($user)){
$tmpArray['email'] = $tmpUser['email'];
}
array_push($users, $tmpArray);
}
$xml = new XmlWriter2();
$xml->push('gexf', array('xmlns' => 'http://www.gexf.net/1.2draft" version="1.2'));
$xml->push('meta', array('lastmodifieddate' => '2009-03-20'));
$xml->element('creator', 'Gexf.net');
$xml->element('description', 'A hello world! file');
$xml->pop();
$xml->push('graph', array('mode' => 'static', 'defaultedgetype' => 'directed'));
$xml->push('nodes');
for($i = 0; $i < count($users); $i++){
$xml->push('node', array('id' => $users['id'],
'label' => $users['email']));$xml->pop();
}
$xml->pop();
$xml->push('edges');
while($tmp = mysql_fetch_array($data2)){
$xml->push('edge', array('id' => id,
'source' => $tmp['sender'], 'target' => $tmp['recipient'], 'weight'
=> $tmp['numMails']));$xml->pop();
$id++;
}
$xml->pop();
$xml->pop();
$xml->pop();
print $xml->getXml();
?>
And it works, the code is correct, but it takes hours. Really, even after 30min it is not finished doing all that. And I have no idea how to improve it and get it very fast. Or is there another possiblity to get the data from the mysql database in the right format without using php?
Please help me. My deadline is really close and I have no ideas and didn't find anything on the web that fits my problem.
So you doing over 500 000 querys, that a big overhead,
try just join in the email in the first query:
/* puggan added left join email */
$data1 = mysql_query("SELECT DISTINCT mailToId.sId, users.email FROM mailToId LEFT JOIN users ON (users.id = mailToId.sId) WHERE date BETWEEN '01.06.2002' AND '30.06.2002' UNION SELECT DISTINCT mailToId.rId, users.email FROM mailToId LEFT JOIN users ON (users.id = mailToId.rId) WHERE date BETWEEN '01.06.2002' AND '30.06.2002'") or die(mysql_error());
$data2 = mysql_query("SELECT sender, recipient, count(*) AS numMails FROM mailTo WHERE date BETWEEN '01.06.2002' AND '30.06.2002' GROUP BY sender, recipient") or die (mysql_error());
$users = array();
$id = 0;
while($tmpUsers = mysql_fetch_array($data1))
{
$tmpArray['id'] = $tmpUsers['sId'];
/* Puggan added: */
$tmpArray['email'] = $tmpUsers['email'];
// $user = mysql_query("SELECT email FROM users WHERE id=".$tmpUsers['sId']);
// while($tmpUser = mysql_fetch_array($user))
// {
// $tmpArray['email'] = $tmpUser['email'];
// }
array_push($users, $tmpArray);
}

Categories